e2D 1.3.7__tar.gz → 1.3.9__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: e2D
3
- Version: 1.3.7
3
+ Version: 1.3.9
4
4
  Summary: Python library for 2D games. Streamlines dev with keyboard/mouse input, vector calculations, color manipulation, and collision detection. Simplify game creation and unleash creativity!
5
5
  Home-page: https://github.com/marick-py/e2D
6
6
  Author: Riccardo Mariani
@@ -25,7 +25,7 @@ pg.font.init()
25
25
  font_arial_16 = pg.font.SysFont("Arial", 16)
26
26
  font_arial_32 = pg.font.SysFont("Arial", 32)
27
27
  font_arial_64 = pg.font.SysFont("Arial", 64)
28
- my_arial_font_size = lambda size: pg.font.SysFont("Arial", size)
28
+ create_arial_font_size = lambda size: pg.font.SysFont("Arial", size)
29
29
 
30
30
  TEXT_FIXED_SIDES_TOP_LEFT = 0
31
31
  TEXT_FIXED_SIDES_TOP_MIDDLE = 1
e2D-1.3.9/e2D/plots.py ADDED
@@ -0,0 +1,355 @@
1
+ from __future__ import annotations
2
+ from .envs import *
3
+ import numpy as np
4
+
5
+ def no_error_complex_function(function, args) -> V2|Vector2D:
6
+ res :complex= function(args)
7
+ return V2(res.real, res.imag)
8
+
9
+ sign = lambda value: -1 if value < 0 else (1 if value > 0 else 0)
10
+
11
+ class Function:
12
+ def __init__(self, function, color:list[float]|tuple[float,float,float]=(255,255,255)) -> None:
13
+ self.plot : Plot
14
+ self.color = color
15
+ self.function = function
16
+ self.__layer_surface__ :pg.Surface= None #type: ignore
17
+
18
+ def update_points(self) -> None:
19
+ self.update_function(self.function)
20
+
21
+ def get_points(self) -> list:
22
+ signs_self = np.sign(self.function(*self.plot.meshgrid))
23
+ 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))
24
+ return np.column_stack(np.where(((-4 < signs_sum) & (signs_sum < 4))[:-1, 1:])[::-1]) / self.plot.scale()
25
+
26
+ def update_function(self, new_function) -> None:
27
+ self.function = new_function
28
+ self.points = self.get_points()
29
+ self.render()
30
+
31
+ def get_derivative(self, delta:float=.01, color:None|list[float]|tuple[float,float,float]=None) -> Function:
32
+ return Function(lambda x,y: (self.function(x + delta, y) - self.function(x,y))/delta - y, color if color != None else self.color)
33
+
34
+ def render(self) -> None:
35
+ self.__layer_surface__.fill((0,0,0,0))
36
+ 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
37
+ if any(x < 1 for x in self.plot.scale):
38
+ # draw rects
39
+ for point in self.points:
40
+ pg.draw.rect(self.__layer_surface__, self.color, (point.tolist() + offset)() + self.plot.pixel_size()) #type: ignore
41
+ else:
42
+ # draw points
43
+ for point in self.points:
44
+ point = point.astype(int).tolist()
45
+ if self.plot.dragging != None:
46
+ point = round(point + offset)()
47
+ self.__layer_surface__.set_at(point, self.color)
48
+
49
+ def draw(self) -> None:
50
+ self.plot.canvas.blit(self.__layer_surface__, (0,0))
51
+
52
+ # class ComplexFunction:
53
+ # 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:
54
+ # self.auto_connect_treshold = auto_connect_treshold
55
+ # self.plot = plot
56
+ # self.starting_t = starting_t
57
+ # self.ending_t = ending_t
58
+ # self.color = color
59
+ # self.step = step
60
+ # self.points_color = points_color
61
+ # self.points_radius = points_radius
62
+ # self.update_function(function)
63
+
64
+ # def update_points(self) -> None:
65
+ # self.update_function(self.function)
66
+
67
+ # def update_function(self, new_function) -> None:
68
+ # self.function = new_function
69
+ # 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)]
70
+ # self.full_auto_connect = not any(point.distance_to(self.points[i]) > self.auto_connect_treshold for i,point in enumerate(self.points[1:]))
71
+
72
+ # def draw(self) -> None:
73
+ # if self.points_radius:
74
+ # for point in self.points:
75
+ # pg.draw.circle(self.plot.canvas, self.color if self.points_color == None else self.points_color, self.plot.__plot2real__(point)(), self.points_radius)
76
+
77
+ # if len(self.points) < 2: return
78
+ # if self.full_auto_connect:
79
+ # pg.draw.lines(self.plot.canvas, self.color, False, [self.plot.__plot2real__(point)() for point in self.points])
80
+ # else:
81
+ # real_points = [self.plot.__plot2real__(point)() for point in self.points]
82
+ # for i,(point, real_point) in enumerate(zip(self.points[1:], real_points[1:])):
83
+ # if point.distance_to(self.points[i]) < self.auto_connect_treshold:
84
+ # pg.draw.line(self.plot.canvas, self.color, real_points[i], real_point) #type: ignore
85
+
86
+ class __PlotSettings__:
87
+ def __init__(self, plot:Plot) -> None:
88
+ self.plot = plot
89
+ self.settings :dict= {
90
+ # axes
91
+ "distance_to_axis_for_scalar_zoom" : 10,
92
+
93
+ # cursor
94
+ "show_cursor_coords" : False,
95
+ "zoom_on_center" : False,
96
+
97
+ # plot visual options
98
+ # plot system
99
+ "use_inter_pixel_correction" : True,
100
+ "use_real_time_rendering" : True,
101
+
102
+ # axes
103
+ "change_axes_colors_on_mouse_hover" : True,
104
+ "mouse_hover_axes_color" : rgb(200, 200, 200),
105
+ "show_axes" : True,
106
+ "show_x_axis" : True,
107
+ "show_y_axis" : True,
108
+ "axes_default_color" : rgb(100, 100, 100),
109
+ "x_axis_color" : None,
110
+ "y_axis_color" : None,
111
+ "axes_default_width" : 5,
112
+ "x_axis_width" : None,
113
+ "y_axis_width" : None,
114
+ "render_axes_on_top" : False,
115
+
116
+ # grid
117
+ "show_grid" : True,
118
+ "grid_step" : V2(PI, 1),
119
+ "grid_color" : rgb(17, 65, 68),
120
+ "grid_width" : 1,
121
+
122
+ # pointer
123
+ "show_pointer" : True,
124
+ "pointer_radius" : 15,
125
+ "pointer_color" : rgb(255, 255, 255),
126
+
127
+ #rect
128
+ "render_bg" : True,
129
+ "bg_color" : rgb(28, 29, 34),
130
+ "draw_rect" : True,
131
+ "rect_color" : rgb(255, 255, 255),
132
+ "rect_width" : 5,
133
+ "show_corners_coords" : True,
134
+
135
+ # info options
136
+ "show_zoom_info": True,
137
+ "top_left_info_position" : self.plot.position + V2(20, 100),
138
+ "info_interline_space" : V2(0, 32),
139
+ "info_precision" : 2,
140
+ }
141
+
142
+ def print_current_settings(self) -> None:
143
+ longest_key = max(map(len, self.settings))
144
+ longest_type = max(map(lambda setting: len(str(type(setting)).split("'")[1]), self.settings.values()))
145
+ split_string = "'"
146
+ texts = [
147
+ f"{setting}{' '*(longest_key-len(setting))} :{str(type(self.settings[setting])).split(split_string)[1]}{' '*(longest_type-len(str(type(self.settings[setting])).split(split_string)[1]))}=\t{self.settings[setting]}"
148
+ for setting in self.settings
149
+ ]
150
+ longest_text = max(map(len , texts))
151
+ print("=" * longest_text)
152
+ for text in texts:
153
+ print(text)
154
+ print("=" * longest_text)
155
+
156
+ def toggle(self, key:str) -> None:
157
+ self.set(key, not self.get(key))
158
+
159
+ def set(self, key:str, new_value) -> None:
160
+ if not (key in self.settings): raise ValueError(f"The key [{key}] does not exist...")
161
+ self.settings[key] = new_value
162
+
163
+ def multiple_set(self, new_key_and_values_dict:dict) -> None:
164
+ self.settings.update(new_key_and_values_dict)
165
+
166
+ def get(self, key:str) -> bool|V2|Vector2D|int|float:
167
+ return self.settings[key]
168
+
169
+ def multiple_get(self, keys:list[str]) -> list[bool|V2|Vector2D|int|float]:
170
+ return [self.get(key) for key in keys]
171
+
172
+ class Plot:
173
+ __y_axis_multiplier__ = V2(1, -1)
174
+ 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:
175
+ self.rootEnv = rootEnv
176
+
177
+ self.top_left_plot_coord = top_left_plot_coord
178
+ self.bottom_right_plot_coord = bottom_right_plot_coord
179
+
180
+ self.position = plot_position
181
+ self.size = plot_size
182
+ self.scale = scale
183
+
184
+ self.settings = __PlotSettings__(self)
185
+
186
+ self.current_zoom = V2one * -np.log2(10)*10
187
+ self.current_offset = V2(0,0)
188
+ self.update_grid(True)
189
+
190
+ self.functions :list[Function]= []
191
+
192
+ self.canvas = pg.Surface(self.size(), pg.SRCALPHA, 32).convert_alpha()
193
+ self.dragging = None
194
+ self.start_dragging = V2z
195
+ self.is_mouse_in_rect = False
196
+ self.mouse_scalar = V2one.copy()
197
+
198
+ def set_borders_by_position_and_zoom(self) -> None:
199
+ self.top_left_plot_coord = self.current_offset - (.5**(.1*self.current_zoom)) * self.__y_axis_multiplier__
200
+ self.bottom_right_plot_coord = self.current_offset + (.5**(.1*self.current_zoom)) * self.__y_axis_multiplier__
201
+ self.top_left_x, self.top_left_y = self.top_left_plot_coord
202
+ self.bottom_right_x, self.bottom_right_y = self.bottom_right_plot_coord
203
+
204
+ def update_grid(self, update_step_grid=False) -> None:
205
+ self.set_borders_by_position_and_zoom()
206
+ if update_step_grid:
207
+ self.step = (self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size / self.scale
208
+ 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)
209
+ self.meshgrid = np.meshgrid(X, Y)
210
+ self.pixel_size = abs(self.size / (self.bottom_right_plot_coord - self.top_left_plot_coord) * (self.step * -1))
211
+ if self.settings.get("use_inter_pixel_correction"):
212
+ self.pixel_size += V2one
213
+
214
+ def load_function(self, function:Function) -> None:
215
+ function.plot = self
216
+ function.__layer_surface__ = pg.Surface(self.size(), pg.SRCALPHA, 32).convert_alpha()
217
+ function.update_function(function.function)
218
+ self.functions.append(function)
219
+
220
+ def __plot2real__(self, plot_position:V2|Vector2D) -> V2|Vector2D:
221
+ return (plot_position + self.top_left_plot_coord * -1) * self.size / (self.bottom_right_plot_coord - self.top_left_plot_coord)
222
+
223
+ def __real2plot__(self, real_position:V2|Vector2D) -> V2|Vector2D:
224
+ return (real_position - self.position) * (self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size + self.top_left_plot_coord
225
+
226
+ def render(self) -> None:
227
+ # fill canvas with alpha zero color
228
+ self.canvas.fill((0,0,0,0))
229
+
230
+ # draw grid
231
+ if self.settings.get("show_grid"):
232
+ grid_step = self.settings.get("grid_step")
233
+ grid_color = self.settings.get("grid_color")
234
+ grid_width = self.settings.get("grid_width")
235
+ clamped_top_left = grid_step * (self.top_left_plot_coord / grid_step).__ceil__()
236
+ clamped_bottom_right = grid_step * (self.bottom_right_plot_coord / grid_step).__ceil__()
237
+ for x_value in np.arange(clamped_top_left.x, clamped_bottom_right.x, grid_step.x):
238
+ pg.draw.line( self.canvas, grid_color, self.__plot2real__((x_value, self.top_left_y))(), self.__plot2real__((x_value, self.bottom_right_y))(), grid_width)
239
+ for y_value in np.arange(clamped_bottom_right.y, clamped_top_left.y, grid_step.y):
240
+ 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)
241
+
242
+ # draw functions
243
+ for function in self.functions: function.draw()
244
+
245
+ # draw rect, pointer and corner coords
246
+ if self.settings.get("draw_rect"):
247
+ pg.draw.rect(self.canvas, self.settings.get("rect_color"), V2z() + self.size(), self.settings.get("rect_width"))
248
+
249
+ if self.settings.get("show_pointer"):
250
+ center = self.size * .5
251
+ aimer_radius = self.settings.get("pointer_radius")
252
+ pointer_color = self.settings.get("pointer_color")
253
+ pg.draw.line(self.canvas, pointer_color, (center + aimer_radius)(), (center - aimer_radius)(), 1)
254
+ pg.draw.line(self.canvas, pointer_color, (center + self.__y_axis_multiplier__ * aimer_radius)(), (center - self.__y_axis_multiplier__ * aimer_radius)(), 1)
255
+ pg.draw.circle(self.canvas, pointer_color, (self.size * .5)(), 15, 1)
256
+
257
+ if self.settings.get("show_corners_coords"):
258
+ self.rootEnv.print(str(self.top_left_plot_coord), 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)
259
+ self.rootEnv.print(str(V2(self.top_left_plot_coord.x, self.bottom_right_plot_coord.y)), 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)
260
+ self.rootEnv.print(str(self.bottom_right_plot_coord), 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)
261
+ self.rootEnv.print(str(V2(self.bottom_right_plot_coord.x, self.top_left_plot_coord.y)), 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)
262
+
263
+ def update(self) -> None:
264
+ # update mouse and center positions
265
+ self.plot_mouse_position = self.__real2plot__(self.rootEnv.mouse.position)
266
+ self.plot_center_real_position = self.__plot2real__(V2z) + self.position
267
+
268
+ self.is_mouse_in_rect = self.position.x < self.rootEnv.mouse.position.x < self.position.x + self.size.x and \
269
+ self.position.y < self.rootEnv.mouse.position.y < self.position.y + self.size.y
270
+
271
+ # render the functions when i stop dragging (when i release the left mouse key)
272
+ if self.rootEnv.mouse.just_released[0] and self.dragging != None:
273
+ self.dragging = None
274
+ self.update_grid(True)
275
+ for function in self.functions:
276
+ function.update_points()
277
+ self.render()
278
+
279
+ if self.is_mouse_in_rect:
280
+ # 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.
281
+ 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
282
+ if self.mouse_scalar.x == self.mouse_scalar.y == 0: self.mouse_scalar = V2one
283
+ for event in self.rootEnv.events:
284
+ if event.type == pg.MOUSEWHEEL:
285
+ if self.settings.get("zoom_on_center"):
286
+ # this will always zoom exactly in the middle of the canvas, according to the current plot offset
287
+ self.current_zoom += event.y * self.mouse_scalar
288
+ else:
289
+ # this will get the pre plot mouse position, calculate the zoom and with the "after" mouse position calculate the offset that i will add to the current plot offset
290
+ pre = self.__real2plot__(self.rootEnv.mouse.position)
291
+ self.current_zoom += event.y * self.mouse_scalar
292
+ # i have to update the corners of the plot here to use the real2plot function correctly (i cant use shortcuts)
293
+ self.update_grid(False)
294
+ self.current_offset += pre - self.__real2plot__(self.rootEnv.mouse.position)
295
+ self.update_grid(True)
296
+ for function in self.functions:
297
+ function.update_points()
298
+ self.render()
299
+
300
+ # start dragging whenever mouse left button is just pressed
301
+ if self.rootEnv.mouse.just_pressed[0] and self.dragging == None:
302
+ self.dragging = self.rootEnv.mouse.position.copy()
303
+ self.start_dragging = self.dragging.copy()
304
+
305
+ # update the canvas if im dragging
306
+ if self.dragging:
307
+ offset = (self.dragging - self.rootEnv.mouse.position)* V2(1, -1) * (abs(self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size)
308
+ self.dragging = self.rootEnv.mouse.position.copy()
309
+ self.current_offset += offset
310
+
311
+ # with real time rendering i update the function render each frame whnever im dragging the canvs around
312
+ if self.settings.get("use_real_time_rendering"):
313
+ self.update_grid(True)
314
+ for function in self.functions: function.update_points()
315
+ else:
316
+ self.update_grid()
317
+ self.render()
318
+
319
+ def draw(self) -> None:
320
+ # fill canvas with bg color
321
+ if self.settings.get("render_bg"):
322
+ self.rootEnv.screen.fill(self.settings.get("bg_color"), self.position() + self.size())
323
+
324
+ # render functions before axes
325
+ if render_axes_on_top:=self.settings.get("render_axes_on_top"): self.rootEnv.screen.blit(self.canvas, self.position())
326
+
327
+ # render axes
328
+ if self.top_left_x < 0 < self.bottom_right_x and (self.settings.get("show_x_axis") and self.settings.get("show_axes")):
329
+ pg.draw.line(self.rootEnv.screen,
330
+ (self.settings.get("axes_default_color") if (x_color:=self.settings.get("x_axis_color"))==None else x_color) if self.mouse_scalar.x else (self.settings.get("mouse_hover_axes_color")),
331
+ (self.__plot2real__(V2(0, self.top_left_y)) + self.position)(),
332
+ (self.__plot2real__(V2(0, self.bottom_right_y)) + self.position)(),
333
+ self.settings.get("axes_default_width") if (x_width:=self.settings.get("x_axis_width"))==None else x_width) #type: ignore
334
+ if self.bottom_right_y < 0 < self.top_left_y and (self.settings.get("show_y_axis") and self.settings.get("show_axes")):
335
+ pg.draw.line(self.rootEnv.screen,
336
+ (self.settings.get("axes_default_color") if (y_color:=self.settings.get("y_axis_color"))==None else y_color) if self.mouse_scalar.y else (self.settings.get("mouse_hover_axes_color")),
337
+ (self.__plot2real__(V2(self.top_left_x, 0)) + self.position)(),
338
+ (self.__plot2real__(V2(self.bottom_right_x, 0)) + self.position)(),
339
+ self.settings.get("axes_default_width") if (y_width:=self.settings.get("y_axis_width"))==None else y_width) #type: ignore
340
+
341
+ # render functions after axes
342
+ if not render_axes_on_top: self.rootEnv.screen.blit(self.canvas, self.position())
343
+
344
+ if self.is_mouse_in_rect and self.settings.get("show_cursor_coords"):
345
+ self.rootEnv.print(str(self.plot_mouse_position), self.rootEnv.mouse.position, fixed_sides=TEXT_FIXED_SIDES_BOTTOM_MIDDLE) #type: ignore
346
+
347
+ data = [
348
+ [f"ZOOM:", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
349
+ [f" x: {(.5**(.1*self.current_zoom.x)):.{self.settings.get('info_precision')}f};", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
350
+ [f" y: {(.5**(.1*self.current_zoom.y)):.{self.settings.get('info_precision')}f};", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
351
+ ]
352
+
353
+ for i, (d, fixed_side, show) in enumerate(data):
354
+ if show:
355
+ self.rootEnv.print(d, self.settings.get("top_left_info_position") + self.settings.get("info_interline_space") * i, fixed_sides=fixed_side) #type: ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: e2D
3
- Version: 1.3.7
3
+ Version: 1.3.9
4
4
  Summary: Python library for 2D games. Streamlines dev with keyboard/mouse input, vector calculations, color manipulation, and collision detection. Simplify game creation and unleash creativity!
5
5
  Home-page: https://github.com/marick-py/e2D
6
6
  Author: Riccardo Mariani
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = e2D
3
- version = 1.3.7
3
+ version = 1.3.9
4
4
  author = Riccardo Mariani
5
5
  author_email = ricomari2006@gmail.com
6
6
  description = Python library for 2D games. Streamlines dev with keyboard/mouse input, vector calculations, color manipulation, and collision detection. Simplify game creation and unleash creativity!
e2D-1.3.7/e2D/plots.py DELETED
@@ -1,274 +0,0 @@
1
- from __future__ import annotations
2
- from .envs import *
3
- import numpy as np
4
-
5
- def no_error_complex_function(function, args) -> V2|Vector2D:
6
- res :complex= function(args)
7
- return V2(res.real, res.imag)
8
-
9
- sign = lambda value: -1 if value < 0 else (1 if value > 0 else 0)
10
-
11
- class Function:
12
- def __init__(self, function, color) -> None:
13
- self.plot : Plot
14
- self.color = color
15
- self.function = function
16
- self.__layer_surface__ :pg.Surface= None #type: ignore
17
-
18
- def update_points(self) -> None:
19
- self.update_function(self.function)
20
-
21
- def get_points(self) -> list:
22
- signs_self = np.sign(self.function(*self.plot.meshgrid))
23
- 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))
24
- return np.column_stack(np.where(((-4 < signs_sum) & (signs_sum < 4))[:-1, 1:])[::-1]) / self.plot.scale()
25
-
26
- def update_function(self, new_function) -> None:
27
- self.function = new_function
28
- self.points = self.get_points()
29
- self.render()
30
-
31
- def render(self) -> None:
32
- self.__layer_surface__.fill((0,0,0,0))
33
- offset = self.plot.dragging - self.plot.start_dragging if (self.plot.dragging != None) and (not self.plot.settings.get("use_real_time_rendering")) else None
34
- if any(x < 1 for x in self.plot.scale):
35
- for point in self.points:
36
- # radius = max(min(self.plot.pixel_size)*.5, 1)
37
- # pg.draw.circle(self.plot.canvas, self.color, (point + self.plot.pixel_size*.5)(), radius)
38
- pg.draw.rect(self.__layer_surface__, self.color, (point.tolist() + (offset if offset != None else V2z))() + self.plot.pixel_size()) #type: ignore
39
- else:
40
- for point in self.points:
41
- point = point.astype(int).tolist()
42
- if self.plot.dragging != None:
43
- point = round(point + offset)()
44
- self.__layer_surface__.set_at(point, self.color)
45
-
46
- def draw(self) -> None:
47
- self.plot.canvas.blit(self.__layer_surface__, (0,0))
48
-
49
- # class ComplexFunction:
50
- # 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:
51
- # self.auto_connect_treshold = auto_connect_treshold
52
- # self.plot = plot
53
- # self.starting_t = starting_t
54
- # self.ending_t = ending_t
55
- # self.color = color
56
- # self.step = step
57
- # self.points_color = points_color
58
- # self.points_radius = points_radius
59
- # self.update_function(function)
60
-
61
- # def update_points(self) -> None:
62
- # self.update_function(self.function)
63
-
64
- # def update_function(self, new_function) -> None:
65
- # self.function = new_function
66
- # 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)]
67
- # self.full_auto_connect = not any(point.distance_to(self.points[i]) > self.auto_connect_treshold for i,point in enumerate(self.points[1:]))
68
-
69
- # def draw(self) -> None:
70
- # if self.points_radius:
71
- # for point in self.points:
72
- # pg.draw.circle(self.plot.canvas, self.color if self.points_color == None else self.points_color, self.plot.__plot2real__(point)(), self.points_radius)
73
-
74
- # if len(self.points) < 2: return
75
- # if self.full_auto_connect:
76
- # pg.draw.lines(self.plot.canvas, self.color, False, [self.plot.__plot2real__(point)() for point in self.points])
77
- # else:
78
- # real_points = [self.plot.__plot2real__(point)() for point in self.points]
79
- # for i,(point, real_point) in enumerate(zip(self.points[1:], real_points[1:])):
80
- # if point.distance_to(self.points[i]) < self.auto_connect_treshold:
81
- # pg.draw.line(self.plot.canvas, self.color, real_points[i], real_point) #type: ignore
82
-
83
- class __PlotSettings__:
84
- def __init__(self, plot:Plot) -> None:
85
- self.plot = plot
86
- self.settings :dict= {
87
- "use_real_time_rendering" : True,
88
- "show_corners_coords" : True,
89
-
90
- "use_inter_pixel_correction" : True,
91
-
92
- "show_zoom_info": True,
93
- "top_left_info_position" : self.plot.position + V2(20, 100),
94
- "info_spacing" : V2(0, 32),
95
- "info_precision" : 2,
96
-
97
- "distance_to_axis_for_scalar_zoom" : 10,
98
-
99
- "bg_color" : (25, 25, 25),
100
- "axes_default_color" : (100, 100, 100),
101
- "x_axis_color" : None,
102
- "y_axis_color" : None,
103
-
104
- "axes_default_width" : 5,
105
- "x_axis_width" : None,
106
- "y_axis_width" : None,
107
-
108
- "show_cursor_coords" : False,
109
- }
110
-
111
- def print_current_settings(self) -> None:
112
- longest_key = max(map(len, self.settings))
113
- longest_type = max(map(lambda setting: len(str(type(setting)).split("'")[1]), self.settings.values()))
114
- split_string = '"'
115
- for setting in self.settings:
116
- print(f"{setting}{' '*(longest_key-len(setting))} :{str(type(self.settings[setting])).split(split_string)[1]}{' '*(longest_type-len(str(type(self.settings[setting])).split(split_string)[1]))}=\t{self.settings[setting]}")
117
-
118
- def set(self, key:str, new_value) -> None:
119
- if not (key in self.settings): raise ValueError(f"The key [{key}] does not exist...")
120
- self.settings[key] = new_value
121
-
122
- def multiple_set(self, keys:list[str], new_values:list) -> None:
123
- for key, new_value in zip(keys, new_values):
124
- self.set(key, new_value)
125
-
126
- def get(self, key:str) -> bool|V2|Vector2D|int|float:
127
- return self.settings[key]
128
-
129
- def multiple_get(self, keys:list[str]) -> list[bool|V2|Vector2D|int|float]:
130
- return [self.get(key) for key in keys]
131
-
132
- class Plot:
133
- __top_left_multiplier__ = V2(1, -1)
134
- __bottop_right_multiplier__ = V2(1, -1)
135
- 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:
136
- self.rootEnv = rootEnv
137
-
138
- self.top_left_plot_coord = top_left_plot_coord
139
- self.bottom_right_plot_coord = bottom_right_plot_coord
140
-
141
- self.position = plot_position
142
- self.size = plot_size
143
- self.scale = scale
144
-
145
- self.settings = __PlotSettings__(self)
146
-
147
- self.current_zoom = V2one * -np.log2(10)*10
148
- self.current_offset = V2(0,0)
149
- self.update_grid(True)
150
-
151
- self.functions :list[Function]= []
152
-
153
- self.canvas = pg.Surface(self.size())
154
- self.dragging = None
155
- self.start_dragging = V2z
156
- self.is_mouse_in_rect = False
157
-
158
- def set_borders_by_position_and_zoom(self) -> None:
159
- self.top_left_plot_coord = self.current_offset - (.5**(.1*self.current_zoom)) * self.__top_left_multiplier__
160
- self.bottom_right_plot_coord = self.current_offset + (.5**(.1*self.current_zoom)) * self.__bottop_right_multiplier__
161
- self.top_left_x, self.top_left_y = self.top_left_plot_coord
162
- self.bottom_right_x, self.bottom_right_y = self.bottom_right_plot_coord
163
-
164
- def update_grid(self, update_step_grid=False) -> None:
165
- self.set_borders_by_position_and_zoom()
166
- if update_step_grid:
167
- self.step = (self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size / self.scale
168
- 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)
169
- self.meshgrid = np.meshgrid(X, Y)
170
- self.pixel_size = abs(self.size / (self.bottom_right_plot_coord - self.top_left_plot_coord) * (self.step * -1))
171
- if self.settings.get("use_inter_pixel_correction"):
172
- self.pixel_size += V2one
173
-
174
- def load_function(self, function:Function) -> None:
175
- function.plot = self
176
- function.__layer_surface__ = pg.Surface(self.size(), pg.SRCALPHA, 32).convert_alpha()
177
- function.update_function(function.function)
178
- self.functions.append(function)
179
-
180
- def __plot2real__(self, plot_position:V2|Vector2D) -> V2|Vector2D:
181
- return (plot_position + self.top_left_plot_coord * -1) * self.size / (self.bottom_right_plot_coord - self.top_left_plot_coord)
182
-
183
- def __real2plot__(self, real_position:V2|Vector2D) -> V2|Vector2D:
184
- return (real_position - self.position) * (self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size + self.top_left_plot_coord
185
-
186
- def render(self) -> None:
187
- self.canvas.fill(self.settings.get("bg_color")) #type: ignore
188
- if self.top_left_x < 0 < self.bottom_right_x:
189
- pg.draw.line(self.canvas,
190
- self.settings.get("axes_default_color") if (x_color:=self.settings.get("x_axis_color"))==None else x_color, #type: ignore
191
- self.__plot2real__(V2(0, self.top_left_y))(),
192
- self.__plot2real__(V2(0, self.bottom_right_y))(),
193
- self.settings.get("axes_default_width") if (x_width:=self.settings.get("x_axis_width"))==None else x_width) #type: ignore
194
- if self.bottom_right_y < 0 < self.top_left_y:
195
- pg.draw.line(self.canvas,
196
- self.settings.get("axes_default_color") if (y_color:=self.settings.get("y_axis_color"))==None else y_color, #type: ignore
197
- self.__plot2real__(V2(self.top_left_x, 0))(),
198
- self.__plot2real__(V2(self.bottom_right_x, 0))(),
199
- self.settings.get("axes_default_width") if (y_width:=self.settings.get("y_axis_width"))==None else y_width) #type: ignore
200
-
201
- for function in self.functions: function.draw()
202
-
203
- pg.draw.rect(self.canvas, (255,255,255), V2z() + self.size(), 5) #type: ignore
204
-
205
- center = self.size * .5
206
- aimer_radius = 15
207
- pg.draw.line(self.canvas, (100,100,100), (center + aimer_radius)(), (center - aimer_radius)(), 1)
208
- pg.draw.line(self.canvas, (100,100,100), (center + self.__top_left_multiplier__ * aimer_radius)(), (center - self.__top_left_multiplier__ * aimer_radius)(), 1)
209
- pg.draw.circle(self.canvas, (100,100,100), (self.size * .5)(), 15, 1)
210
-
211
- if self.settings.get("show_corners_coords"):
212
- self.rootEnv.print(str(self.top_left_plot_coord.__round__(.1)), 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)
213
- self.rootEnv.print(str(V2(self.top_left_plot_coord.x, self.bottom_right_plot_coord.y).__round__(.1)), 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)
214
- self.rootEnv.print(str(self.bottom_right_plot_coord.__round__(.1)), 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)
215
- self.rootEnv.print(str(V2(self.bottom_right_plot_coord.x, self.top_left_plot_coord.y).__round__(.1)), 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)
216
-
217
- def update(self) -> None:
218
- self.plot_mouse_position = self.__real2plot__(self.rootEnv.mouse.position)
219
- self.plot_center_real_position = self.__plot2real__(V2z) + self.position
220
-
221
- self.is_mouse_in_rect = self.position.x < self.rootEnv.mouse.position.x < self.position.x + self.size.x and \
222
- self.position.y < self.rootEnv.mouse.position.y < self.position.y + self.size.y
223
-
224
- if self.rootEnv.mouse.just_released[0] and self.dragging != None:
225
- self.dragging = None
226
- self.update_grid(True)
227
- for function in self.functions:
228
- function.update_points()
229
- self.render()
230
-
231
-
232
- if self.is_mouse_in_rect:
233
- for event in self.rootEnv.events:
234
- if event.type == pg.MOUSEWHEEL:
235
-
236
- range_n = self.settings.get("distance_to_axis_for_scalar_zoom")
237
- scalar = V2(0 if abs(self.plot_center_real_position.x - self.rootEnv.mouse.position.x) < range_n else 1, 0 if abs(self.plot_center_real_position.y - self.rootEnv.mouse.position.y) < range_n else 1) #type: ignore
238
- self.current_zoom += event.y * scalar
239
-
240
- self.update_grid(True)
241
- for function in self.functions:
242
- function.update_points()
243
- self.render()
244
-
245
- if self.rootEnv.mouse.just_pressed[0] and self.dragging == None:
246
- self.dragging = self.rootEnv.mouse.position.copy()
247
- self.start_dragging = self.dragging.copy()
248
-
249
- if self.dragging:
250
- offset = (self.dragging - self.rootEnv.mouse.position)* V2(1, -1) * (abs(self.bottom_right_plot_coord - self.top_left_plot_coord) / self.size)
251
- self.dragging = self.rootEnv.mouse.position.copy()
252
- self.current_offset += offset
253
-
254
- if self.settings.get("use_real_time_rendering"):
255
- self.update_grid(True)
256
- for function in self.functions: function.update_points()
257
- else:
258
- self.update_grid()
259
- self.render()
260
-
261
- def draw(self) -> None:
262
- self.rootEnv.screen.blit(self.canvas, self.position())
263
- if self.is_mouse_in_rect and self.settings.get("show_cursor_coords"):
264
- self.rootEnv.print(str(round(self.plot_mouse_position, .1)), self.rootEnv.mouse.position, fixed_sides=TEXT_FIXED_SIDES_BOTTOM_MIDDLE) #type: ignore
265
-
266
- data = [
267
- [f"ZOOM:", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
268
- [f" x: {self.current_zoom.x:.{self.settings.get('info_precision')}f};", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
269
- [f" y: {self.current_zoom.y:.{self.settings.get('info_precision')}f};", TEXT_FIXED_SIDES_TOP_LEFT, self.settings.get("show_zoom_info")],
270
- ]
271
-
272
- for i, (d, fixed_side, show) in enumerate(data):
273
- if show:
274
- self.rootEnv.print(d, self.settings.get("top_left_info_position") + self.settings.get("info_spacing") * i, fixed_sides=fixed_side) #type: ignore
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes