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/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.append(self)
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.append(self)
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:V2|Vector2D|Point, point_b:V2|Vector2D|Point, color:list[float]|tuple[float,float,float]=(255,255,255), width:float=1) -> None:
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, position, label:str="", radius:float=1, color:list[float]|tuple[float,float,float]=(255,255,255),
70
- label_color:tuple[float, float, float]=(255, 255, 255), label_position_offset:Vector2D|V2=V2z, label_fixed_sides:int=TEXT_FIXED_SIDES_TOP_LEFT, label_font:pg.font.Font=FONT_ARIAL_32, label_bg_color:tuple[int,int,int]|list[int]|None=None, label_border_color:tuple[int,int,int]|list[int]|None=None, label_border_width:float=0, label_border_radius:int|list[int]|tuple[int,int,int,int]=-1, label_margin:V2=V2z) -> None:
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.label_fixed_sides = label_fixed_sides
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 = V2z.copy()
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, fixed_sides=self.label_fixed_sides, 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__)
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, function, domain:list[float]=[-np.inf, np.inf], codomain:list[float]=[-np.inf, np.inf], color:list[float]|tuple[float,float,float]=(255,255,255)) -> None:
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 = [((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]
117
- codomain = [((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]
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]))] #type: ignore
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 V2z
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 V2z
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[V2|Vector2D]=[], points_color:list[float]|tuple[float,float,float]=(255,0,0), color:list[float]|tuple[float,float,float]=(255,255,255)) -> None:
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[V2|Vector2D]|None=None) -> None:
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) -> V2|Vector2D:
249
+ def no_error_complex_function(function, args) -> Vector2D:
218
250
  res :complex= function(args)
219
- return V2(res.real, res.imag)
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[V2|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)]
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" : V2(PI, 1),
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 + V2(15, 75),
313
- "info_interline_space" : V2(0, 24),
314
- "info_font" : new_font(24),
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:str) -> None:
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:str, new_value) -> None:
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:str) -> bool|V2|Vector2D|int|float:
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[str]) -> list[bool|V2|Vector2D|int|float]:
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__ = V2(1, -1)
350
- def __init__(self, rootEnv:"RootEnv", plot_position:V2|Vector2D, plot_size:V2|Vector2D, top_left_plot_coord:V2|Vector2D, bottom_right_plot_coord: V2|Vector2D, scale:V2|Vector2D=V2one) -> None:
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 :list[Function]= []
359
- self.objects :list[Object]= []
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 = V2z
394
+ self.start_dragging = Vector2D.new_zero()
363
395
  self.is_mouse_in_rect = False
364
- self.mouse_scalar = V2one.copy()
365
- self.plot_mouse_position = V2z.copy()
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
- self.pixel_size += V2one
383
-
384
- def load_function(self, function:Function) -> None:
385
- function.__post_load_init__(self)
386
- def add_function(self, function:Function) -> None:
387
- self.load_function(function)
388
-
389
- def load_object(self, obj:Object) -> None:
390
- obj.__post_load_init__(self, self)
391
- def add_object(self, function:Object) -> None:
392
- self.load_object(function)
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.load_object(d)
442
+ self.add_objects(d)
398
443
  elif isinstance(d, Function):
399
- self.load_function(d)
444
+ self.add_functions(d)
400
445
 
401
- def __plot2real__(self, plot_position:V2|Vector2D) -> V2|Vector2D:
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:V2|Vector2D) -> V2|Vector2D:
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"), V2z() + self.size(), self.settings.get("rect_width")) #type: ignore
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), V2z.copy(), bg_color=(0,0,0), border_color=(255,255,255), border_width=2, border_radius=15, margin=V2(10,10), personalized_surface=self.canvas)
442
- self.rootEnv.print(V2(self.top_left_plot_coord.x, self.bottom_right_plot_coord.y).advanced_stringify(4, True), self.size * V2(0, 1), fixed_sides=TEXT_FIXED_SIDES_BOTTOM_LEFT, bg_color=(0,0,0), border_color=(255,255,255), border_width=2, border_radius=15, margin=V2(10,10), personalized_surface=self.canvas)
443
- self.rootEnv.print(self.bottom_right_plot_coord.advanced_stringify(4, True), self.size.copy(), fixed_sides=TEXT_FIXED_SIDES_BOTTOM_RIGHT, bg_color=(0,0,0), border_color=(255,255,255), border_width=2, border_radius=15, margin=V2(10,10), personalized_surface=self.canvas)
444
- self.rootEnv.print(V2(self.bottom_right_plot_coord.x, self.top_left_plot_coord.y).advanced_stringify(4, True), self.size * V2(1, 0), fixed_sides=TEXT_FIXED_SIDES_TOP_RIGHT, bg_color=(0,0,0), border_color=(255,255,255), border_width=2, border_radius=15, margin=V2(10,10), personalized_surface=self.canvas)
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__(V2z) + self.position
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.just_released[0] and self.dragging != None:
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 = V2(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
463
- if self.mouse_scalar.x == self.mouse_scalar.y == 0: self.mouse_scalar = V2one
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.just_pressed[0] and self.dragging == None:
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.set_position(V2(self.position.x + self.size.x, self.rootEnv.mouse.position.y))
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.set_position(V2(self.position.x, self.rootEnv.mouse.position.y))
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.set_position(V2(self.rootEnv.mouse.position.x, self.position.y + self.size.y))
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.set_position(V2(self.rootEnv.mouse.position.x, self.position.y))
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)* V2(1, -1) * (abs(self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size)
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) -> None:
557
+ def get_humanoid_zoom(self) -> Vector2D:
513
558
  return 2 ** (-.1*self.current_zoom)
514
559
 
515
- def focus(self, center:V2|Vector2D|None=None, zoom:float|Vector2D|V2|None=None) -> None:
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 any(isinstance(zoom, cls) for cls in {Vector2D, V2}):
520
- self.current_zoom = V2(np.log2(zoom.x), np.log2(zoom.y)) * -10
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 = V2one * -np.log2(zoom)*10
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:V2|Vector2D|None=None, bottom_right_plot_coord: V2|Vector2D|None=None) -> None:
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)/2,
532
- (bottom_right_plot_coord - top_left_plot_coord)/2 * self.__y_axis_multiplier__
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()) #type: ignore
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__(V2(0, self.top_left_y)) + self.position)(),
548
- (self.__plot2real__(V2(0, self.bottom_right_y)) + self.position)(),
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__(V2(self.top_left_x, 0)) + self.position)(),
554
- (self.__plot2real__(V2(self.bottom_right_x, 0)) + self.position)(),
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, fixed_sides=TEXT_FIXED_SIDES_BOTTOM_MIDDLE) #type: ignore
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
- [f"ZOOM:", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
568
- [f" x: {str_current_real_zoom[0]};", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
569
- [f" y: {str_current_real_zoom[1]};", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
570
- [f" ratio: {optimize_value_string(current_real_zoom.x / current_real_zoom.y, 4)};", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
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, fixed_side, show) in enumerate(data):
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, fixed_sides=fixed_side, font=self.settings.get("info_font"))
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"))