e2D 1.4.10__py3-none-any.whl → 1.4.12__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.
- e2D/__init__.py +356 -1032
- e2D/__init__.pyi +1176 -0
- e2D/envs.py +96 -146
- e2D/plots.py +145 -101
- e2D/utils.py +169 -69
- e2D/winrec.py +1 -5
- {e2D-1.4.10.dist-info → e2D-1.4.12.dist-info}/METADATA +2 -2
- e2D-1.4.12.dist-info/RECORD +12 -0
- {e2D-1.4.10.dist-info → e2D-1.4.12.dist-info}/WHEEL +1 -1
- e2D-1.4.10.dist-info/RECORD +0 -11
- {e2D-1.4.10.dist-info → e2D-1.4.12.dist-info}/LICENSE +0 -0
- {e2D-1.4.10.dist-info → e2D-1.4.12.dist-info}/top_level.txt +0 -0
e2D/plots.py
CHANGED
|
@@ -2,16 +2,22 @@ from __future__ import annotations
|
|
|
2
2
|
from .envs import *
|
|
3
3
|
import numpy as np
|
|
4
4
|
|
|
5
|
+
__LITERAL_PIVOT_POSITIONS__ = Literal["top_left", "top_center", "top_right", "center_left", "center_center", "center_right", "bottom_left", "bottom_center", "bottom_right"]
|
|
6
|
+
__PIVOT_POSITIONS_MULTIPLIER__ = dict(zip(("top_left", "top_center", "top_right", "center_left", "center_center", "center_right", "bottom_left", "bottom_center", "bottom_right"), (Vector2D(x,y) for y in [0, .5, 1] for x in [0, .5, 1])))
|
|
7
|
+
|
|
8
|
+
__LITERAL_SETTINGS_KEYS__ = Literal['distance_to_axis_for_scalar_zoom','zoom_on_center','warp_mouse','movable','zoomable','use_inter_pixel_correction','use_real_time_rendering','change_axes_colors_on_mouse_hover','mouse_hover_axes_color','show_axes','show_x_axis','show_y_axis','axes_default_color','x_axis_color','y_axis_color','axes_default_width','x_axis_width','y_axis_width','render_axes_on_top','show_grid','grid_step','grid_color','grid_width','show_pointer','pointer_radius','pointer_color','show_cursor_coords','render_bg','bg_color','draw_rect','rect_color','rect_width','show_corners_coords','show_zoom_info','top_left_info_position','info_interline_space','info_font','info_precision']
|
|
9
|
+
|
|
5
10
|
class Function:
|
|
6
11
|
def __init__(self) -> None:
|
|
7
12
|
self.plot : Plot
|
|
13
|
+
self.id = int|str
|
|
8
14
|
self.__layer_surface__ :pg.Surface= None #type: ignore
|
|
9
15
|
|
|
10
16
|
def __post_load_init__(self, plot:Plot) -> None:
|
|
11
17
|
self.plot = plot
|
|
12
18
|
self.__layer_surface__ = pg.Surface(self.plot.size(), pg.SRCALPHA, 32).convert_alpha()
|
|
13
19
|
self.update()
|
|
14
|
-
self.plot.functions.
|
|
20
|
+
self.plot.functions[self.id] = self
|
|
15
21
|
|
|
16
22
|
def update(self) -> None: pass
|
|
17
23
|
|
|
@@ -23,6 +29,7 @@ class Function:
|
|
|
23
29
|
class Object:
|
|
24
30
|
def __init__(self) -> None:
|
|
25
31
|
self.plot : Plot
|
|
32
|
+
self.id : int|str
|
|
26
33
|
self.__layer_surface__ :pg.Surface= None #type: ignore
|
|
27
34
|
self.__controller__ = None
|
|
28
35
|
|
|
@@ -30,7 +37,7 @@ class Object:
|
|
|
30
37
|
self.plot = plot
|
|
31
38
|
self.__layer_surface__ = pg.Surface(self.plot.size(), pg.SRCALPHA, 32).convert_alpha()
|
|
32
39
|
self.__controller__ = controller
|
|
33
|
-
self.plot.objects.
|
|
40
|
+
self.plot.objects[self.id] = self
|
|
34
41
|
if isinstance(self, Line):
|
|
35
42
|
self.point_a.__post_load_init__(self.plot, self)
|
|
36
43
|
self.point_b.__post_load_init__(self.plot, self)
|
|
@@ -43,8 +50,9 @@ class Object:
|
|
|
43
50
|
self.plot.canvas.blit(self.__layer_surface__, (0,0))
|
|
44
51
|
|
|
45
52
|
class Line(Object):
|
|
46
|
-
def __init__(self, point_a:
|
|
53
|
+
def __init__(self, id:int|str, point_a:Vector2D|Point, point_b:Vector2D|Point, color:list[float]|tuple[float,float,float]=(255,255,255), width:float=1) -> None:
|
|
47
54
|
super().__init__()
|
|
55
|
+
self.id = id
|
|
48
56
|
if isinstance(point_a, Point):
|
|
49
57
|
self.point_a = point_a
|
|
50
58
|
else:
|
|
@@ -66,16 +74,31 @@ class Line(Object):
|
|
|
66
74
|
pg.draw.line(self.__layer_surface__, self.color, self.point_a.center(), self.point_b.center(), self.width)
|
|
67
75
|
|
|
68
76
|
class Point(Object):
|
|
69
|
-
def __init__(self,
|
|
70
|
-
|
|
77
|
+
def __init__(self,
|
|
78
|
+
id : int|str,
|
|
79
|
+
position : Vector2D,
|
|
80
|
+
label : str = "",
|
|
81
|
+
radius : float = 1,
|
|
82
|
+
color : list[float]|tuple[float,float,float] = (255,255,255),
|
|
83
|
+
label_color : tuple[float, float, float] = (255, 255, 255),
|
|
84
|
+
label_position_offset : Vector2D = Vector2D.zero(),
|
|
85
|
+
label_pivot_position : __LITERAL_PIVOT_POSITIONS__ = "top_left",
|
|
86
|
+
label_font : pg.font.Font = FONT_ARIAL_32,
|
|
87
|
+
label_bg_color : tuple[int,int,int]|list[int]|None = None,
|
|
88
|
+
label_border_color : tuple[int,int,int]|list[int]|None = None,
|
|
89
|
+
label_border_width : float = 0,
|
|
90
|
+
label_border_radius : int|list[int]|tuple[int,int,int,int] = -1,
|
|
91
|
+
label_margin : Vector2D = Vector2D.zero()
|
|
92
|
+
) -> None:
|
|
71
93
|
super().__init__()
|
|
94
|
+
self.id = id
|
|
72
95
|
self.position = position
|
|
73
96
|
self.radius = radius
|
|
74
97
|
self.color = color
|
|
75
98
|
self.label = label
|
|
76
99
|
self.label_color = label_color
|
|
77
100
|
self.label_position_offset = label_position_offset
|
|
78
|
-
self.
|
|
101
|
+
self.label_pivot_position = label_pivot_position
|
|
79
102
|
self.label_font = label_font
|
|
80
103
|
self.label_bg_color = label_bg_color
|
|
81
104
|
self.label_border_color = label_border_color
|
|
@@ -84,7 +107,7 @@ class Point(Object):
|
|
|
84
107
|
self.label_margin = label_margin
|
|
85
108
|
|
|
86
109
|
self.rect :list[float]= [0, 0, 0, 0]
|
|
87
|
-
self.center =
|
|
110
|
+
self.center = Vector2D.new_zero()
|
|
88
111
|
|
|
89
112
|
def update(self) -> None:
|
|
90
113
|
radius = self.radius * self.plot.size / (self.plot.bottom_right_plot_coord - self.plot.top_left_plot_coord) * self.plot.__y_axis_multiplier__
|
|
@@ -97,11 +120,17 @@ class Point(Object):
|
|
|
97
120
|
def __render__(self) -> None:
|
|
98
121
|
self.__layer_surface__.fill((0,0,0,0))
|
|
99
122
|
pg.draw.ellipse(self.__layer_surface__, self.color, self.rect)
|
|
100
|
-
self.plot.rootEnv.print(self.label, self.text_position, color=self.label_color,
|
|
123
|
+
self.plot.rootEnv.print(self.label, self.text_position, color=self.label_color, pivot_position=self.label_pivot_position, font=self.label_font, bg_color=self.label_bg_color, border_color=self.label_border_color, border_width=self.label_border_width, border_radius=self.label_border_radius, margin=self.label_margin, personalized_surface=self.__layer_surface__)
|
|
101
124
|
|
|
102
125
|
class MathFunction(Function):
|
|
103
|
-
def __init__(self,
|
|
126
|
+
def __init__(self,
|
|
127
|
+
id:int|str,
|
|
128
|
+
function:Callable[[int|float, int|float], int|float],
|
|
129
|
+
domain:list[float]=[-np.inf, np.inf],
|
|
130
|
+
codomain:list[float]=[-np.inf, np.inf], colo
|
|
131
|
+
:list[float]|tuple[float,float,float]=(255,255,255)) -> None:
|
|
104
132
|
super().__init__()
|
|
133
|
+
self.id = id
|
|
105
134
|
self.color = color
|
|
106
135
|
self.function = function
|
|
107
136
|
self.domain = domain
|
|
@@ -113,17 +142,18 @@ class MathFunction(Function):
|
|
|
113
142
|
# credits for the plotting idea:
|
|
114
143
|
# https://www.youtube.com/watch?v=EvvWOaLgKVU
|
|
115
144
|
# mattbatwings (https://www.youtube.com/@mattbatwings)
|
|
116
|
-
domain =
|
|
117
|
-
codomain =
|
|
145
|
+
domain = tuple(((domain - self.plot.top_left_plot_coord.x) * self.plot.size.x / (self.plot.bottom_right_plot_coord.x - self.plot.top_left_plot_coord.x)) for domain in self.domain)
|
|
146
|
+
codomain = tuple(((codomain - self.plot.top_left_plot_coord.y) * self.plot.size.y / (self.plot.bottom_right_plot_coord.y - self.plot.top_left_plot_coord.y)) for codomain in self.codomain)
|
|
118
147
|
|
|
119
148
|
signs_sum = signs_self + np.roll(signs_self, axis=1, shift=1) + np.roll(signs_self, axis=0, shift=-1) + np.roll(signs_self, axis=(1,0), shift=(1,-1))
|
|
120
149
|
coords = np.column_stack(np.where(((-4 < signs_sum) & (signs_sum < 4))[:-1, 1:])[::-1]) / self.plot.scale()
|
|
150
|
+
|
|
121
151
|
return coords[
|
|
122
152
|
np.logical_and(
|
|
123
153
|
np.logical_and(coords[:, 0] >= domain[0], coords[:, 0] <= domain[1]),
|
|
124
|
-
np.logical_and(coords[:, 1] >= codomain[0], coords[:, 1] <= codomain[1]))]
|
|
154
|
+
np.logical_and(coords[:, 1] >= codomain[0], coords[:, 1] <= codomain[1]))]
|
|
125
155
|
|
|
126
|
-
def update(self, new_function=None, render=True, domain:list[float]|None=None, codomain:list[float]|None=None) -> None:
|
|
156
|
+
def update(self, new_function:None|Callable[[int|float, int|float], int|float]=None, render=True, domain:list[float]|None=None, codomain:list[float]|None=None) -> None:
|
|
127
157
|
if new_function != None:
|
|
128
158
|
self.function = new_function
|
|
129
159
|
if domain != None: self.domain = domain
|
|
@@ -137,7 +167,7 @@ class MathFunction(Function):
|
|
|
137
167
|
|
|
138
168
|
def __render__(self) -> None:
|
|
139
169
|
self.__layer_surface__.fill((0,0,0,0))
|
|
140
|
-
offset = self.plot.dragging - self.plot.start_dragging if (self.plot.dragging != None) and (not self.plot.settings.get("use_real_time_rendering")) else
|
|
170
|
+
offset = self.plot.dragging - self.plot.start_dragging if (self.plot.dragging != None) and (not self.plot.settings.get("use_real_time_rendering")) else Vector2D.zero()
|
|
141
171
|
if any(x < 1 for x in self.plot.scale):
|
|
142
172
|
# draw rects
|
|
143
173
|
for point in self.points:
|
|
@@ -151,8 +181,9 @@ class MathFunction(Function):
|
|
|
151
181
|
self.__layer_surface__.set_at(point, self.color) #type: ignore
|
|
152
182
|
|
|
153
183
|
class TimeFunction(Function):
|
|
154
|
-
def __init__(self, function, t_range:list[float]=[0,0, 1.0], t_step:float=.01, color:list[float]|tuple[float,float,float]=(255,255,255)) -> None:
|
|
184
|
+
def __init__(self, id:int|str, function, t_range:list[float]=[0,0, 1.0], t_step:float=.01, color:list[float]|tuple[float,float,float]=(255,255,255)) -> None:
|
|
155
185
|
super().__init__()
|
|
186
|
+
self.id = id
|
|
156
187
|
self.color = color
|
|
157
188
|
self.function = function
|
|
158
189
|
self.t_range = t_range
|
|
@@ -177,7 +208,7 @@ class TimeFunction(Function):
|
|
|
177
208
|
self.__render__()
|
|
178
209
|
def __render__(self) -> None:
|
|
179
210
|
self.__layer_surface__.fill((0,0,0,0))
|
|
180
|
-
offset = self.plot.dragging - self.plot.start_dragging if (self.plot.dragging != None) and (not self.plot.settings.get("use_real_time_rendering")) else
|
|
211
|
+
offset = self.plot.dragging - self.plot.start_dragging if (self.plot.dragging != None) and (not self.plot.settings.get("use_real_time_rendering")) else Vector2D.zero()
|
|
181
212
|
if any(x < 1 for x in self.plot.scale):
|
|
182
213
|
# draw rects
|
|
183
214
|
for point in self.points:
|
|
@@ -191,13 +222,14 @@ class TimeFunction(Function):
|
|
|
191
222
|
self.__layer_surface__.set_at(point, self.color) #type: ignore
|
|
192
223
|
|
|
193
224
|
class PointsFunction(Function):
|
|
194
|
-
def __init__(self, points:list[
|
|
225
|
+
def __init__(self, id:int|str, points:list[Vector2D]=[], points_color:list[float]|tuple[float,float,float]=(255,0,0), color:list[float]|tuple[float,float,float]=(255,255,255)) -> None:
|
|
195
226
|
super().__init__()
|
|
227
|
+
self.id = id
|
|
196
228
|
self.color = color
|
|
197
229
|
self.points = points
|
|
198
230
|
self.points_color = points_color
|
|
199
231
|
|
|
200
|
-
def update(self, points:list[
|
|
232
|
+
def update(self, points:list[Vector2D]|None=None) -> None:
|
|
201
233
|
if points != None: self.points = points
|
|
202
234
|
self.plot_points = [self.plot.__plot2real__(point)() for point in self.points if \
|
|
203
235
|
self.plot.top_left_x < point.x < self.plot.bottom_right_x and \
|
|
@@ -214,9 +246,9 @@ class PointsFunction(Function):
|
|
|
214
246
|
# 5)
|
|
215
247
|
|
|
216
248
|
"""
|
|
217
|
-
def no_error_complex_function(function, args) ->
|
|
249
|
+
def no_error_complex_function(function, args) -> Vector2D:
|
|
218
250
|
res :complex= function(args)
|
|
219
|
-
return
|
|
251
|
+
return Vector2D(res.real, res.imag)
|
|
220
252
|
sign = lambda value: -1 if value < 0 else (1 if value > 0 else 0)
|
|
221
253
|
class ComplexFunction:
|
|
222
254
|
def __init__(self, function, plot:"Plot", starting_t:float=-10, ending_t:float=10, step=.01, color=(255,255,255), auto_connect_treshold=float("inf"), points_radius=2, points_color=None) -> None:
|
|
@@ -235,7 +267,7 @@ class ComplexFunction:
|
|
|
235
267
|
|
|
236
268
|
def update_function(self, new_function) -> None:
|
|
237
269
|
self.function = new_function
|
|
238
|
-
self.points :list[
|
|
270
|
+
self.points :list[Vector2D]= [point for t in range(int(self.starting_t / self.step), int(self.ending_t / self.step)) if (self.plot.bottom_right_y < (point:=no_error_complex_function(new_function, t * self.step)).y < self.plot.top_left_y) and (self.plot.top_left_x < point.x < self.plot.bottom_right_x)]
|
|
239
271
|
self.full_auto_connect = not any(point.distance_to(self.points[i]) > self.auto_connect_treshold for i,point in enumerate(self.points[1:]))
|
|
240
272
|
|
|
241
273
|
def draw(self) -> None:
|
|
@@ -256,7 +288,7 @@ class ComplexFunction:
|
|
|
256
288
|
class __PlotSettings__:
|
|
257
289
|
def __init__(self, plot:Plot) -> None:
|
|
258
290
|
self.plot = plot
|
|
259
|
-
self.settings :dict= {
|
|
291
|
+
self.settings :dict[__LITERAL_SETTINGS_KEYS__, int|float|bool|Vector2D|tuple|list|pg.font.Font]= {
|
|
260
292
|
# axes
|
|
261
293
|
"distance_to_axis_for_scalar_zoom" : 10,
|
|
262
294
|
|
|
@@ -287,7 +319,7 @@ class __PlotSettings__:
|
|
|
287
319
|
|
|
288
320
|
# grid
|
|
289
321
|
"show_grid" : True,
|
|
290
|
-
"grid_step" :
|
|
322
|
+
"grid_step" : Vector2D(PI, 1),
|
|
291
323
|
"grid_color" : rgb(17, 65, 68),
|
|
292
324
|
"grid_width" : 1,
|
|
293
325
|
|
|
@@ -309,9 +341,9 @@ class __PlotSettings__:
|
|
|
309
341
|
|
|
310
342
|
# info options
|
|
311
343
|
"show_zoom_info": True,
|
|
312
|
-
"top_left_info_position" : self.plot.position +
|
|
313
|
-
"info_interline_space" :
|
|
314
|
-
"info_font" :
|
|
344
|
+
"top_left_info_position" : self.plot.position + Vector2D(15, 75),
|
|
345
|
+
"info_interline_space" : Vector2D(0, 24),
|
|
346
|
+
"info_font" : NEW_FONT(24),
|
|
315
347
|
"info_precision" : 2,
|
|
316
348
|
}
|
|
317
349
|
|
|
@@ -329,25 +361,25 @@ class __PlotSettings__:
|
|
|
329
361
|
print(text)
|
|
330
362
|
print("=" * longest_text)
|
|
331
363
|
|
|
332
|
-
def toggle(self, key:
|
|
364
|
+
def toggle(self, key:__LITERAL_SETTINGS_KEYS__) -> None:
|
|
333
365
|
self.set(key, not self.get(key))
|
|
334
366
|
|
|
335
|
-
def set(self, key:
|
|
367
|
+
def set(self, key:__LITERAL_SETTINGS_KEYS__, new_value:int|float|bool|Vector2D|tuple|list|pg.font.Font) -> None:
|
|
336
368
|
if not (key in self.settings): raise ValueError(f"The key [{key}] does not exist...")
|
|
337
369
|
self.settings[key] = new_value
|
|
338
370
|
|
|
339
|
-
def multiple_set(self, new_key_and_values_dict:dict) -> None:
|
|
371
|
+
def multiple_set(self, new_key_and_values_dict:dict[__LITERAL_SETTINGS_KEYS__, int|float|bool|Vector2D|tuple|list|pg.font.Font]) -> None:
|
|
340
372
|
self.settings.update(new_key_and_values_dict)
|
|
341
373
|
|
|
342
|
-
def get(self, key:
|
|
374
|
+
def get(self, key:__LITERAL_SETTINGS_KEYS__) -> int|float|bool|Vector2D|tuple|list|pg.font.Font:
|
|
343
375
|
return self.settings[key]
|
|
344
376
|
|
|
345
|
-
def multiple_get(self, keys:list[
|
|
377
|
+
def multiple_get(self, keys:list[__LITERAL_SETTINGS_KEYS__]) -> list[int|float|bool|Vector2D|tuple|list|pg.font.Font]:
|
|
346
378
|
return [self.get(key) for key in keys]
|
|
347
379
|
|
|
348
380
|
class Plot:
|
|
349
|
-
__y_axis_multiplier__ =
|
|
350
|
-
def __init__(self, rootEnv:"RootEnv", plot_position:
|
|
381
|
+
__y_axis_multiplier__ = Vector2D(1, -1)
|
|
382
|
+
def __init__(self, rootEnv:"RootEnv", plot_position:Vector2D, plot_size:Vector2D, top_left_plot_coord:Vector2D, bottom_right_plot_coord: Vector2D, scale:Vector2D=Vector2D.one()) -> None:
|
|
351
383
|
self.rootEnv = rootEnv
|
|
352
384
|
self.top_left_plot_coord = top_left_plot_coord
|
|
353
385
|
self.bottom_right_plot_coord = bottom_right_plot_coord
|
|
@@ -355,14 +387,14 @@ class Plot:
|
|
|
355
387
|
self.size = plot_size
|
|
356
388
|
self.scale = scale
|
|
357
389
|
self.settings = __PlotSettings__(self)
|
|
358
|
-
self.functions :
|
|
359
|
-
self.objects :
|
|
390
|
+
self.functions :dict[int|str, Function]= {}
|
|
391
|
+
self.objects :dict[int|str, Object]= {}
|
|
360
392
|
self.canvas = pg.Surface(self.size(), pg.SRCALPHA, 32).convert_alpha()
|
|
361
393
|
self.dragging = None
|
|
362
|
-
self.start_dragging =
|
|
394
|
+
self.start_dragging = Vector2D.new_zero()
|
|
363
395
|
self.is_mouse_in_rect = False
|
|
364
|
-
self.mouse_scalar =
|
|
365
|
-
self.plot_mouse_position =
|
|
396
|
+
self.mouse_scalar = Vector2D.new_one()
|
|
397
|
+
self.plot_mouse_position = Vector2D.new_zero()
|
|
366
398
|
self.focus_using_corners(top_left_plot_coord, bottom_right_plot_coord)
|
|
367
399
|
|
|
368
400
|
def set_borders_by_position_and_zoom(self) -> None:
|
|
@@ -371,37 +403,50 @@ class Plot:
|
|
|
371
403
|
self.top_left_x, self.top_left_y = self.top_left_plot_coord
|
|
372
404
|
self.bottom_right_x, self.bottom_right_y = self.bottom_right_plot_coord
|
|
373
405
|
|
|
374
|
-
def update_grid(self, update_step_grid=False) -> None:
|
|
406
|
+
def update_grid(self, update_step_grid:bool=False) -> None:
|
|
375
407
|
self.set_borders_by_position_and_zoom()
|
|
376
408
|
if update_step_grid:
|
|
377
409
|
self.step = (self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size / self.scale
|
|
378
410
|
X, Y = np.arange(self.top_left_plot_coord.x, self.bottom_right_plot_coord.x, self.step.x), np.arange(self.top_left_plot_coord.y, self.bottom_right_plot_coord.y, self.step.y)
|
|
379
411
|
self.meshgrid = np.meshgrid(X, Y)
|
|
380
412
|
self.pixel_size = abs(self.size / (self.bottom_right_plot_coord - self.top_left_plot_coord) * (self.step * -1))
|
|
381
|
-
if self.settings.get("use_inter_pixel_correction"):
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
def
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
413
|
+
if self.settings.get("use_inter_pixel_correction"): self.pixel_size.add(both=1)
|
|
414
|
+
|
|
415
|
+
def add_functions(self, *functions:Function) -> None:
|
|
416
|
+
for func in functions:
|
|
417
|
+
func.__post_load_init__(self)
|
|
418
|
+
def remove_function(self, *functions:int|str|Function) -> None:
|
|
419
|
+
for fid in functions:
|
|
420
|
+
if fid in self.functions:
|
|
421
|
+
del self.functions[fid]
|
|
422
|
+
elif isinstance(fid, Function):
|
|
423
|
+
del self.functions[fid.id]
|
|
424
|
+
else:
|
|
425
|
+
raise Exception(f"Unknown util type: {fid}")
|
|
426
|
+
|
|
427
|
+
def add_objects(self, *objects:Object) -> None:
|
|
428
|
+
for obj in objects:
|
|
429
|
+
obj.__post_load_init__(self, self)
|
|
430
|
+
def remove_function(self, *objects:int|str|Object) -> None:
|
|
431
|
+
for oid in objects:
|
|
432
|
+
if oid in self.objects:
|
|
433
|
+
del self.objects[oid]
|
|
434
|
+
elif isinstance(oid, Object):
|
|
435
|
+
del self.functions[oid.id]
|
|
436
|
+
else:
|
|
437
|
+
raise Exception(f"Unknown util type: {oid}")
|
|
393
438
|
|
|
394
439
|
def add(self, *data:Object|Function) -> None:
|
|
395
440
|
for d in data:
|
|
396
441
|
if isinstance(d, Object):
|
|
397
|
-
self.
|
|
442
|
+
self.add_objects(d)
|
|
398
443
|
elif isinstance(d, Function):
|
|
399
|
-
self.
|
|
444
|
+
self.add_functions(d)
|
|
400
445
|
|
|
401
|
-
def __plot2real__(self, plot_position:
|
|
446
|
+
def __plot2real__(self, plot_position:Vector2D) -> Vector2D:
|
|
402
447
|
return (plot_position + self.top_left_plot_coord * -1) * self.size / (self.bottom_right_plot_coord - self.top_left_plot_coord)
|
|
403
448
|
|
|
404
|
-
def __real2plot__(self, real_position:
|
|
449
|
+
def __real2plot__(self, real_position:Vector2D) -> Vector2D:
|
|
405
450
|
return (real_position - self.position) * (self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size + self.top_left_plot_coord
|
|
406
451
|
|
|
407
452
|
def render(self) -> None:
|
|
@@ -421,13 +466,13 @@ class Plot:
|
|
|
421
466
|
pg.draw.line(self.canvas, grid_color, self.__plot2real__((self.top_left_x, y_value))(), self.__plot2real__((self.bottom_right_x, y_value))(), grid_width) #type: ignore
|
|
422
467
|
|
|
423
468
|
# draw functions
|
|
424
|
-
for function in self.functions: function.draw()
|
|
469
|
+
for function in self.functions.values(): function.draw()
|
|
425
470
|
# draw objects
|
|
426
|
-
for obj in self.objects: obj.draw()
|
|
471
|
+
for obj in self.objects.values(): obj.draw()
|
|
427
472
|
|
|
428
473
|
# draw rect, pointer and corner coords
|
|
429
474
|
if self.settings.get("draw_rect"):
|
|
430
|
-
pg.draw.rect(self.canvas, self.settings.get("rect_color"),
|
|
475
|
+
pg.draw.rect(self.canvas, self.settings.get("rect_color"), Vector2D.zero()() + self.size(), self.settings.get("rect_width")) #type: ignore
|
|
431
476
|
|
|
432
477
|
if self.settings.get("show_pointer"):
|
|
433
478
|
center = self.size * .5
|
|
@@ -438,29 +483,29 @@ class Plot:
|
|
|
438
483
|
pg.draw.circle(self.canvas, pointer_color, (self.size * .5)(), 15, 1) #type: ignore
|
|
439
484
|
|
|
440
485
|
if self.settings.get("show_corners_coords"):
|
|
441
|
-
self.rootEnv.print(self.top_left_plot_coord.advanced_stringify(4, True),
|
|
442
|
-
self.rootEnv.print(
|
|
443
|
-
self.rootEnv.print(self.bottom_right_plot_coord.advanced_stringify(4, True), self.size.copy
|
|
444
|
-
self.rootEnv.print(
|
|
486
|
+
self.rootEnv.print(self.top_left_plot_coord.advanced_stringify(4, True), Vector2D.zero(), bg_color=(0,0,0), border_color=(255,255,255), border_width=2, border_radius=15, margin=Vector2D(10,10), personalized_surface=self.canvas)
|
|
487
|
+
self.rootEnv.print(Vector2D(self.top_left_plot_coord.x, self.bottom_right_plot_coord.y).advanced_stringify(4, True), self.size * Vector2D(0, 1), pivot_position="bottom_left", bg_color=(0,0,0), border_color=(255,255,255), border_width=2, border_radius=15, margin=Vector2D(10,10), personalized_surface=self.canvas)
|
|
488
|
+
self.rootEnv.print(self.bottom_right_plot_coord.advanced_stringify(4, True), self.size.copy, pivot_position="bottom_right", bg_color=(0,0,0), border_color=(255,255,255), border_width=2, border_radius=15, margin=Vector2D(10,10), personalized_surface=self.canvas)
|
|
489
|
+
self.rootEnv.print(Vector2D(self.bottom_right_plot_coord.x, self.top_left_plot_coord.y).advanced_stringify(4, True), self.size * Vector2D(1, 0), pivot_position="top_right", bg_color=(0,0,0), border_color=(255,255,255), border_width=2, border_radius=15, margin=Vector2D(10,10), personalized_surface=self.canvas)
|
|
445
490
|
|
|
446
491
|
def update(self) -> None:
|
|
447
492
|
# update mouse and center positions
|
|
448
493
|
self.plot_mouse_position = self.__real2plot__(self.rootEnv.mouse.position)
|
|
449
|
-
self.plot_center_real_position = self.__plot2real__(
|
|
494
|
+
self.plot_center_real_position = self.__plot2real__(Vector2D.zero()) + self.position
|
|
450
495
|
|
|
451
496
|
self.is_mouse_in_rect = self.position.x < self.rootEnv.mouse.position.x < self.position.x + self.size.x and \
|
|
452
497
|
self.position.y < self.rootEnv.mouse.position.y < self.position.y + self.size.y
|
|
453
498
|
|
|
454
499
|
# render the functions when i stop dragging (when i release the left mouse key)
|
|
455
|
-
if self.rootEnv.mouse.
|
|
500
|
+
if self.rootEnv.mouse.get_key(0, "just_released") and self.dragging != None:
|
|
456
501
|
self.dragging = None
|
|
457
502
|
self.focus()
|
|
458
503
|
|
|
459
504
|
if self.is_mouse_in_rect:
|
|
460
505
|
if self.settings.get("zoomable"):
|
|
461
506
|
# mouse scalar is needed for checking if the mouse is hovering an axis, in case it is the opposite one zoom value has to be multiplied by 0 so nullifying it.
|
|
462
|
-
self.mouse_scalar =
|
|
463
|
-
if self.mouse_scalar.x == self.mouse_scalar.y == 0: self.mouse_scalar =
|
|
507
|
+
self.mouse_scalar = Vector2D(0 if abs(self.plot_center_real_position.x - self.rootEnv.mouse.position.x) < self.settings.get("distance_to_axis_for_scalar_zoom") else 1, 0 if abs(self.plot_center_real_position.y - self.rootEnv.mouse.position.y) < self.settings.get("distance_to_axis_for_scalar_zoom") else 1) #type: ignore
|
|
508
|
+
if self.mouse_scalar.x == self.mouse_scalar.y == 0: self.mouse_scalar = Vector2D.one()
|
|
464
509
|
for event in self.rootEnv.events:
|
|
465
510
|
if event.type == pg.MOUSEWHEEL:
|
|
466
511
|
if self.settings.get("zoom_on_center"):
|
|
@@ -477,30 +522,30 @@ class Plot:
|
|
|
477
522
|
|
|
478
523
|
if self.settings.get("movable"):
|
|
479
524
|
# start dragging whenever mouse left button is just pressed
|
|
480
|
-
if self.rootEnv.mouse.
|
|
481
|
-
self.dragging = self.rootEnv.mouse.position.copy
|
|
482
|
-
self.start_dragging = self.dragging.copy
|
|
525
|
+
if self.rootEnv.mouse.get_key(0, "just_pressed") and self.dragging == None:
|
|
526
|
+
self.dragging = self.rootEnv.mouse.position.copy
|
|
527
|
+
self.start_dragging = self.dragging.copy
|
|
483
528
|
|
|
484
529
|
# update the canvas if im dragging
|
|
485
530
|
if self.dragging:
|
|
486
531
|
moved = False
|
|
487
532
|
if (not self.is_mouse_in_rect) and self.settings.get("warp_mouse"):
|
|
488
533
|
if self.rootEnv.mouse.position.x < self.position.x:
|
|
489
|
-
self.rootEnv.mouse.
|
|
534
|
+
self.rootEnv.mouse.position = Vector2D(self.position.x + self.size.x, self.rootEnv.mouse.position.y)
|
|
490
535
|
moved = True
|
|
491
536
|
elif self.rootEnv.mouse.position.x > self.position.x + self.size.x:
|
|
492
|
-
self.rootEnv.mouse.
|
|
537
|
+
self.rootEnv.mouse.position = Vector2D(self.position.x, self.rootEnv.mouse.position.y)
|
|
493
538
|
moved = True
|
|
494
539
|
if self.rootEnv.mouse.position.y < self.position.y:
|
|
495
|
-
self.rootEnv.mouse.
|
|
540
|
+
self.rootEnv.mouse.position = Vector2D(self.rootEnv.mouse.position.x, self.position.y + self.size.y)
|
|
496
541
|
moved = True
|
|
497
542
|
elif self.rootEnv.mouse.position.y > self.position.y + self.size.y:
|
|
498
|
-
self.rootEnv.mouse.
|
|
543
|
+
self.rootEnv.mouse.position = Vector2D(self.rootEnv.mouse.position.x, self.position.y)
|
|
499
544
|
moved = True
|
|
500
545
|
if not moved:
|
|
501
|
-
offset = (self.dragging - self.rootEnv.mouse.position)*
|
|
546
|
+
offset = (self.dragging - self.rootEnv.mouse.position)* Vector2D(1, -1) * (abs(self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size)
|
|
502
547
|
self.current_offset += offset
|
|
503
|
-
self.dragging = self.rootEnv.mouse.position.copy
|
|
548
|
+
self.dragging = self.rootEnv.mouse.position.copy
|
|
504
549
|
|
|
505
550
|
# with real time rendering i update the function render each frame whnever im dragging the canvs around
|
|
506
551
|
if self.settings.get("use_real_time_rendering"):
|
|
@@ -509,33 +554,32 @@ class Plot:
|
|
|
509
554
|
self.update_grid()
|
|
510
555
|
self.render()
|
|
511
556
|
|
|
512
|
-
def get_humanoid_zoom(self) ->
|
|
557
|
+
def get_humanoid_zoom(self) -> Vector2D:
|
|
513
558
|
return 2 ** (-.1*self.current_zoom)
|
|
514
559
|
|
|
515
|
-
def focus(self, center:
|
|
516
|
-
if center != None:
|
|
517
|
-
self.current_offset = center.copy()
|
|
560
|
+
def focus(self, center:Vector2D|None=None, zoom:float|Vector2D|None=None) -> None:
|
|
561
|
+
if center != None: self.current_offset = center.copy
|
|
518
562
|
if zoom != None:
|
|
519
|
-
if
|
|
520
|
-
self.current_zoom =
|
|
563
|
+
if isinstance(zoom, Vector2D):
|
|
564
|
+
self.current_zoom = Vector2D(-np.log2(zoom.x) * 10, -np.log2(zoom.y)*10)
|
|
521
565
|
else:
|
|
522
|
-
self.current_zoom =
|
|
523
|
-
|
|
566
|
+
self.current_zoom = Vector2D(-10, -10) * np.log2(zoom)
|
|
567
|
+
|
|
524
568
|
self.update_grid(True)
|
|
525
|
-
for function in self.functions: function.update()
|
|
526
|
-
for obj in self.objects: obj.update()
|
|
569
|
+
for function in self.functions.values(): function.update()
|
|
570
|
+
for obj in self.objects.values(): obj.update()
|
|
527
571
|
self.render()
|
|
528
572
|
|
|
529
|
-
def focus_using_corners(self, top_left_plot_coord:
|
|
573
|
+
def focus_using_corners(self, top_left_plot_coord:Vector2D|None=None, bottom_right_plot_coord: Vector2D|None=None) -> None:
|
|
530
574
|
self.focus(
|
|
531
|
-
(top_left_plot_coord + bottom_right_plot_coord)
|
|
532
|
-
(bottom_right_plot_coord - top_left_plot_coord)
|
|
575
|
+
(top_left_plot_coord + bottom_right_plot_coord).div(both=2),
|
|
576
|
+
(bottom_right_plot_coord - top_left_plot_coord).div(both=2) * self.__y_axis_multiplier__
|
|
533
577
|
)
|
|
534
578
|
|
|
535
579
|
def draw(self) -> None:
|
|
536
580
|
# fill canvas with bg color
|
|
537
581
|
if self.settings.get("render_bg"):
|
|
538
|
-
self.rootEnv.screen.fill(self.settings.get("bg_color"), self.position() + self.size())
|
|
582
|
+
self.rootEnv.screen.fill(self.settings.get("bg_color"), self.position() + self.size())
|
|
539
583
|
|
|
540
584
|
# render functions before axes
|
|
541
585
|
if render_axes_on_top:=self.settings.get("render_axes_on_top"): self.rootEnv.screen.blit(self.canvas, self.position())
|
|
@@ -544,32 +588,32 @@ class Plot:
|
|
|
544
588
|
if self.top_left_x < 0 < self.bottom_right_x and (self.settings.get("show_x_axis") and self.settings.get("show_axes")):
|
|
545
589
|
pg.draw.line(self.rootEnv.screen,
|
|
546
590
|
(self.settings.get("axes_default_color") if (x_color:=self.settings.get("x_axis_color"))==None else x_color) if (self.mouse_scalar.x or not self.settings.get("change_axes_colors_on_mouse_hover")) else (self.settings.get("mouse_hover_axes_color")), #type: ignore
|
|
547
|
-
(self.__plot2real__(
|
|
548
|
-
(self.__plot2real__(
|
|
591
|
+
(self.__plot2real__(Vector2D(0, self.top_left_y)) + self.position)(),
|
|
592
|
+
(self.__plot2real__(Vector2D(0, self.bottom_right_y)) + self.position)(),
|
|
549
593
|
self.settings.get("axes_default_width") if (x_width:=self.settings.get("x_axis_width"))==None else x_width) #type: ignore
|
|
550
594
|
if self.bottom_right_y < 0 < self.top_left_y and (self.settings.get("show_y_axis") and self.settings.get("show_axes")):
|
|
551
595
|
pg.draw.line(self.rootEnv.screen,
|
|
552
596
|
(self.settings.get("axes_default_color") if (y_color:=self.settings.get("y_axis_color"))==None else y_color) if (self.mouse_scalar.y or not self.settings.get("change_axes_colors_on_mouse_hover")) else (self.settings.get("mouse_hover_axes_color")), #type: ignore
|
|
553
|
-
(self.__plot2real__(
|
|
554
|
-
(self.__plot2real__(
|
|
597
|
+
(self.__plot2real__(Vector2D(self.top_left_x, 0)) + self.position)(),
|
|
598
|
+
(self.__plot2real__(Vector2D(self.bottom_right_x, 0)) + self.position)(),
|
|
555
599
|
self.settings.get("axes_default_width") if (y_width:=self.settings.get("y_axis_width"))==None else y_width) #type: ignore
|
|
556
600
|
|
|
557
601
|
# render functions after axes
|
|
558
602
|
if not render_axes_on_top: self.rootEnv.screen.blit(self.canvas, self.position())
|
|
559
603
|
|
|
560
604
|
if self.is_mouse_in_rect and self.settings.get("show_cursor_coords"):
|
|
561
|
-
self.rootEnv.print(self.plot_mouse_position.advanced_stringify(3, True), self.rootEnv.mouse.position,
|
|
605
|
+
self.rootEnv.print(self.plot_mouse_position.advanced_stringify(3, True), self.rootEnv.mouse.position, pivot_position="bottom_center") #type: ignore
|
|
562
606
|
|
|
563
607
|
|
|
564
608
|
current_real_zoom = (.5**(.1*self.current_zoom))
|
|
565
609
|
str_current_real_zoom = current_real_zoom.advanced_stringify(self.settings.get('info_precision'), True, True)
|
|
566
|
-
data = [
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
610
|
+
data :list[tuple[str, __LITERAL_PIVOT_POSITIONS__, bool]]= [
|
|
611
|
+
(f"ZOOM:", "top_left", self.settings.get("show_zoom_info")),
|
|
612
|
+
(f" x: {str_current_real_zoom[0]};", "top_left", self.settings.get("show_zoom_info")),
|
|
613
|
+
(f" y: {str_current_real_zoom[1]};", "top_left", self.settings.get("show_zoom_info")),
|
|
614
|
+
(f" ratio: {optimize_value_string(current_real_zoom.x / current_real_zoom.y, 4)};", "top_left", self.settings.get("show_zoom_info")),
|
|
571
615
|
]
|
|
572
616
|
|
|
573
|
-
for i, (d,
|
|
617
|
+
for i, (d, pivot_position, show) in enumerate(data):
|
|
574
618
|
if show:
|
|
575
|
-
self.rootEnv.print(d, self.settings.get("top_left_info_position") + self.settings.get("info_interline_space") * i,
|
|
619
|
+
self.rootEnv.print(d, self.settings.get("top_left_info_position") + self.settings.get("info_interline_space") * i, pivot_position=pivot_position, font=self.settings.get("info_font"))
|