e2D 2.0.0__cp313-cp313-win_amd64.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.
Binary file
e2D/cvectors.pxd ADDED
@@ -0,0 +1,56 @@
1
+ # cython: language_level=3
2
+
3
+ """
4
+ Cython header file for Vector2D class
5
+ Allows other Cython modules to use this without Python overhead
6
+ """
7
+
8
+ cimport numpy as cnp
9
+
10
+ ctypedef cnp.float64_t DTYPE_t
11
+
12
+ cdef class Vector2D:
13
+ cdef public cnp.ndarray data
14
+ cdef DTYPE_t* _data_ptr
15
+
16
+ # Fast inline methods (no Python overhead)
17
+ cdef inline double _length_squared(self) nogil
18
+ cdef inline double _length(self) nogil
19
+ cdef inline double _dot(self, Vector2D other) nogil
20
+
21
+ # cpdef methods (callable from Python and Cython)
22
+ cpdef Vector2D copy(self)
23
+ cpdef void set(self, double x, double y)
24
+ cpdef void iadd(self, Vector2D other)
25
+ cpdef void isub(self, Vector2D other)
26
+ cpdef void imul(self, double scalar)
27
+ cpdef void idiv(self, double scalar)
28
+ cpdef void imul_vec(self, Vector2D other)
29
+ cpdef void iadd_scalar(self, double scalar)
30
+ cpdef void isub_scalar(self, double scalar)
31
+ cpdef void normalize(self)
32
+ cpdef void clamp_inplace(self, Vector2D min_val, Vector2D max_val)
33
+ cpdef Vector2D add(self, Vector2D other)
34
+ cpdef Vector2D sub(self, Vector2D other)
35
+ cpdef Vector2D mul(self, double scalar)
36
+ cpdef Vector2D mul_vec(self, Vector2D other)
37
+ cpdef Vector2D normalized(self)
38
+ cpdef double dot_product(self, Vector2D other)
39
+ cpdef double distance_to(self, Vector2D other, bint rooted=*)
40
+ cpdef double angle_to(self, Vector2D other)
41
+ cpdef Vector2D rotate(self, double angle)
42
+ cpdef void irotate(self, double angle)
43
+ cpdef Vector2D lerp(self, Vector2D other, double t)
44
+ cpdef Vector2D clamp(self, Vector2D min_val, Vector2D max_val)
45
+ cpdef Vector2D projection(self, Vector2D other)
46
+ cpdef Vector2D reflection(self, Vector2D normal)
47
+ cpdef list to_list(self)
48
+ cpdef tuple to_tuple(self)
49
+
50
+ # Batch operation functions
51
+ cpdef void batch_add_inplace(list vectors, Vector2D displacement)
52
+ cpdef void batch_scale_inplace(list vectors, double scalar)
53
+ cpdef void batch_normalize_inplace(list vectors)
54
+ cpdef cnp.ndarray vectors_to_array(list vectors)
55
+ cpdef list array_to_vectors(cnp.ndarray arr)
56
+
e2D/cvectors.pyx ADDED
@@ -0,0 +1,561 @@
1
+ # cython: language_level=3
2
+ # cython: boundscheck=False
3
+ # cython: wraparound=False
4
+ # cython: nonecheck=False
5
+ # cython: cdivision=True
6
+ # cython: initializedcheck=False
7
+ # cython: overflowcheck=False
8
+
9
+ """
10
+ Ultra-optimized Vector2D class for heavy simulations
11
+ Uses Cython with numpy backend for maximum performance
12
+ """
13
+
14
+ import numpy as np
15
+ cimport numpy as cnp
16
+ cimport cython
17
+ from libc.math cimport sqrt, sin, cos, atan2, floor, ceil, fabs, fmod, pow as c_pow
18
+ from libc.stdlib cimport rand, RAND_MAX
19
+
20
+ cnp.import_array()
21
+
22
+ cdef class Vector2D:
23
+ """
24
+ High-performance 2D vector class optimized for heavy simulations.
25
+
26
+ Uses contiguous numpy arrays and Cython for near-C performance.
27
+ All operations are optimized with no bounds checking and inline C math.
28
+ """
29
+
30
+ def __cinit__(self, double x=0.0, double y=0.0):
31
+ """Initialize vector with x, y components"""
32
+ self.data = np.array([x, y], dtype=np.float64)
33
+ self._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(self.data)
34
+
35
+ # Property accessors with direct memory access
36
+ @property
37
+ def x(self):
38
+ return self._data_ptr[0]
39
+
40
+ @x.setter
41
+ def x(self, double value):
42
+ self._data_ptr[0] = value
43
+
44
+ @property
45
+ def y(self):
46
+ return self._data_ptr[1]
47
+
48
+ @y.setter
49
+ def y(self, double value):
50
+ self._data_ptr[1] = value
51
+
52
+ # Fast inline operations
53
+ @cython.cdivision(True)
54
+ cdef inline double _length_squared(self) nogil:
55
+ """Calculate squared length (no sqrt, faster)"""
56
+ return self._data_ptr[0] * self._data_ptr[0] + self._data_ptr[1] * self._data_ptr[1]
57
+
58
+ @cython.cdivision(True)
59
+ cdef inline double _length(self) nogil:
60
+ """Calculate length"""
61
+ return sqrt(self._data_ptr[0] * self._data_ptr[0] + self._data_ptr[1] * self._data_ptr[1])
62
+
63
+ @property
64
+ def length(self):
65
+ """Vector length (magnitude)"""
66
+ return self._length()
67
+
68
+ @property
69
+ def length_sqrd(self):
70
+ """Squared length (faster, avoids sqrt)"""
71
+ return self._length_squared()
72
+
73
+ cpdef Vector2D copy(self):
74
+ """Fast copy"""
75
+ cdef Vector2D result = Vector2D.__new__(Vector2D)
76
+ result.data = self.data.copy()
77
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
78
+ return result
79
+
80
+ # In-place operations (fastest)
81
+ cpdef void set(self, double x, double y):
82
+ """Set both components"""
83
+ self._data_ptr[0] = x
84
+ self._data_ptr[1] = y
85
+
86
+ cpdef void iadd(self, Vector2D other):
87
+ """In-place addition"""
88
+ self._data_ptr[0] += other._data_ptr[0]
89
+ self._data_ptr[1] += other._data_ptr[1]
90
+
91
+ cpdef void isub(self, Vector2D other):
92
+ """In-place subtraction"""
93
+ self._data_ptr[0] -= other._data_ptr[0]
94
+ self._data_ptr[1] -= other._data_ptr[1]
95
+
96
+ cpdef void imul(self, double scalar):
97
+ """In-place scalar multiplication"""
98
+ self._data_ptr[0] *= scalar
99
+ self._data_ptr[1] *= scalar
100
+
101
+ cpdef void idiv(self, double scalar):
102
+ """In-place division"""
103
+ cdef double inv = 1.0 / scalar
104
+ self._data_ptr[0] *= inv
105
+ self._data_ptr[1] *= inv
106
+
107
+ cpdef void imul_vec(self, Vector2D other):
108
+ """In-place component-wise multiplication"""
109
+ self._data_ptr[0] *= other._data_ptr[0]
110
+ self._data_ptr[1] *= other._data_ptr[1]
111
+
112
+ cpdef void iadd_scalar(self, double scalar):
113
+ """In-place scalar addition"""
114
+ self._data_ptr[0] += scalar
115
+ self._data_ptr[1] += scalar
116
+
117
+ cpdef void isub_scalar(self, double scalar):
118
+ """In-place scalar subtraction"""
119
+ self._data_ptr[0] -= scalar
120
+ self._data_ptr[1] -= scalar
121
+
122
+ cpdef void normalize(self):
123
+ """Normalize in-place"""
124
+ cdef double length
125
+ cdef double inv_length
126
+ length = self._length()
127
+ if length > 0.0:
128
+ inv_length = 1.0 / length
129
+ self._data_ptr[0] *= inv_length
130
+ self._data_ptr[1] *= inv_length
131
+
132
+ cpdef void clamp_inplace(self, Vector2D min_val, Vector2D max_val):
133
+ """Clamp components in-place"""
134
+ if self._data_ptr[0] < min_val._data_ptr[0]:
135
+ self._data_ptr[0] = min_val._data_ptr[0]
136
+ elif self._data_ptr[0] > max_val._data_ptr[0]:
137
+ self._data_ptr[0] = max_val._data_ptr[0]
138
+
139
+ if self._data_ptr[1] < min_val._data_ptr[1]:
140
+ self._data_ptr[1] = min_val._data_ptr[1]
141
+ elif self._data_ptr[1] > max_val._data_ptr[1]:
142
+ self._data_ptr[1] = max_val._data_ptr[1]
143
+
144
+ # New vector operations (return new instances)
145
+ cpdef Vector2D add(self, Vector2D other):
146
+ """Addition (returns new vector)"""
147
+ cdef Vector2D result = Vector2D.__new__(Vector2D)
148
+ result.data = np.empty(2, dtype=np.float64)
149
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
150
+ result._data_ptr[0] = self._data_ptr[0] + other._data_ptr[0]
151
+ result._data_ptr[1] = self._data_ptr[1] + other._data_ptr[1]
152
+ return result
153
+
154
+ cpdef Vector2D sub(self, Vector2D other):
155
+ """Subtraction (returns new vector)"""
156
+ cdef Vector2D result = Vector2D.__new__(Vector2D)
157
+ result.data = np.empty(2, dtype=np.float64)
158
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
159
+ result._data_ptr[0] = self._data_ptr[0] - other._data_ptr[0]
160
+ result._data_ptr[1] = self._data_ptr[1] - other._data_ptr[1]
161
+ return result
162
+
163
+ cpdef Vector2D mul(self, double scalar):
164
+ """Scalar multiplication (returns new vector)"""
165
+ cdef Vector2D result = Vector2D.__new__(Vector2D)
166
+ result.data = np.empty(2, dtype=np.float64)
167
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
168
+ result._data_ptr[0] = self._data_ptr[0] * scalar
169
+ result._data_ptr[1] = self._data_ptr[1] * scalar
170
+ return result
171
+
172
+ cpdef Vector2D mul_vec(self, Vector2D other):
173
+ """Component-wise multiplication (returns new vector)"""
174
+ cdef Vector2D result = Vector2D.__new__(Vector2D)
175
+ result.data = np.empty(2, dtype=np.float64)
176
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
177
+ result._data_ptr[0] = self._data_ptr[0] * other._data_ptr[0]
178
+ result._data_ptr[1] = self._data_ptr[1] * other._data_ptr[1]
179
+ return result
180
+
181
+ cpdef Vector2D normalized(self):
182
+ """Get normalized vector (returns new)"""
183
+ cdef double length
184
+ cdef double inv_length
185
+ cdef Vector2D result
186
+
187
+ length = self._length()
188
+ result = Vector2D.__new__(Vector2D)
189
+ result.data = np.empty(2, dtype=np.float64)
190
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
191
+
192
+ if length > 0.0:
193
+ inv_length = 1.0 / length
194
+ result._data_ptr[0] = self._data_ptr[0] * inv_length
195
+ result._data_ptr[1] = self._data_ptr[1] * inv_length
196
+ else:
197
+ result._data_ptr[0] = 0.0
198
+ result._data_ptr[1] = 0.0
199
+ return result
200
+
201
+ @cython.cdivision(True)
202
+ cdef inline double _dot(self, Vector2D other) nogil:
203
+ """Dot product (inline, no GIL)"""
204
+ return self._data_ptr[0] * other._data_ptr[0] + self._data_ptr[1] * other._data_ptr[1]
205
+
206
+ cpdef double dot_product(self, Vector2D other):
207
+ """Dot product"""
208
+ return self._dot(other)
209
+
210
+ cpdef double distance_to(self, Vector2D other, bint rooted=True):
211
+ """Distance to another vector"""
212
+ cdef double dx
213
+ cdef double dy
214
+ cdef double dist_sq
215
+
216
+ dx = self._data_ptr[0] - other._data_ptr[0]
217
+ dy = self._data_ptr[1] - other._data_ptr[1]
218
+ dist_sq = dx * dx + dy * dy
219
+ if rooted:
220
+ return sqrt(dist_sq)
221
+ return dist_sq
222
+
223
+ cpdef double angle_to(self, Vector2D other):
224
+ """Angle to another vector"""
225
+ cdef double dx
226
+ cdef double dy
227
+
228
+ dx = other._data_ptr[0] - self._data_ptr[0]
229
+ dy = other._data_ptr[1] - self._data_ptr[1]
230
+ return atan2(dy, dx)
231
+
232
+ @property
233
+ def angle(self):
234
+ """Angle of this vector"""
235
+ return atan2(self._data_ptr[1], self._data_ptr[0])
236
+
237
+ @angle.setter
238
+ def angle(self, double new_angle):
239
+ """Set angle while maintaining magnitude"""
240
+ cdef double mag
241
+
242
+ mag = self._length()
243
+ self._data_ptr[0] = mag * cos(new_angle)
244
+ self._data_ptr[1] = mag * sin(new_angle)
245
+
246
+ cpdef Vector2D rotate(self, double angle):
247
+ """Rotate vector by angle (returns new)"""
248
+ cdef double cos_a
249
+ cdef double sin_a
250
+ cdef Vector2D result
251
+
252
+ cos_a = cos(angle)
253
+ sin_a = sin(angle)
254
+ result = Vector2D.__new__(Vector2D)
255
+ result.data = np.empty(2, dtype=np.float64)
256
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
257
+ result._data_ptr[0] = self._data_ptr[0] * cos_a - self._data_ptr[1] * sin_a
258
+ result._data_ptr[1] = self._data_ptr[0] * sin_a + self._data_ptr[1] * cos_a
259
+ return result
260
+
261
+ cpdef void irotate(self, double angle):
262
+ """Rotate in-place"""
263
+ cdef double cos_a
264
+ cdef double sin_a
265
+ cdef double new_x
266
+ cdef double new_y
267
+ cos_a = cos(angle)
268
+ sin_a = sin(angle)
269
+ new_x = self._data_ptr[0] * cos_a - self._data_ptr[1] * sin_a
270
+ new_y = self._data_ptr[0] * sin_a + self._data_ptr[1] * cos_a
271
+ self._data_ptr[0] = new_x
272
+ self._data_ptr[1] = new_y
273
+
274
+ cpdef Vector2D lerp(self, Vector2D other, double t):
275
+ """Linear interpolation"""
276
+ cdef Vector2D result = Vector2D.__new__(Vector2D)
277
+ result.data = np.empty(2, dtype=np.float64)
278
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
279
+ result._data_ptr[0] = self._data_ptr[0] + (other._data_ptr[0] - self._data_ptr[0]) * t
280
+ result._data_ptr[1] = self._data_ptr[1] + (other._data_ptr[1] - self._data_ptr[1]) * t
281
+ return result
282
+
283
+ cpdef Vector2D clamp(self, Vector2D min_val, Vector2D max_val):
284
+ """Clamp (returns new)"""
285
+ cdef Vector2D result = Vector2D.__new__(Vector2D)
286
+ result.data = np.empty(2, dtype=np.float64)
287
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
288
+
289
+ result._data_ptr[0] = self._data_ptr[0]
290
+ if result._data_ptr[0] < min_val._data_ptr[0]:
291
+ result._data_ptr[0] = min_val._data_ptr[0]
292
+ elif result._data_ptr[0] > max_val._data_ptr[0]:
293
+ result._data_ptr[0] = max_val._data_ptr[0]
294
+
295
+ result._data_ptr[1] = self._data_ptr[1]
296
+ if result._data_ptr[1] < min_val._data_ptr[1]:
297
+ result._data_ptr[1] = min_val._data_ptr[1]
298
+ elif result._data_ptr[1] > max_val._data_ptr[1]:
299
+ result._data_ptr[1] = max_val._data_ptr[1]
300
+
301
+ return result
302
+
303
+ cpdef Vector2D projection(self, Vector2D other):
304
+ """Project this vector onto another"""
305
+ cdef double dot
306
+ cdef double other_len_sq
307
+ cdef double scalar
308
+
309
+ dot = self._dot(other)
310
+ other_len_sq = other._length_squared()
311
+ if other_len_sq > 0.0:
312
+ scalar = dot / other_len_sq
313
+ return other.mul(scalar)
314
+ return Vector2D(0.0, 0.0)
315
+
316
+ cpdef Vector2D reflection(self, Vector2D normal):
317
+ """Reflect vector across normal"""
318
+ cdef double dot
319
+ cdef Vector2D result
320
+
321
+ dot = self._dot(normal)
322
+ result = Vector2D.__new__(Vector2D)
323
+ result.data = np.empty(2, dtype=np.float64)
324
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
325
+ result._data_ptr[0] = self._data_ptr[0] - 2.0 * dot * normal._data_ptr[0]
326
+ result._data_ptr[1] = self._data_ptr[1] - 2.0 * dot * normal._data_ptr[1]
327
+ return result
328
+
329
+ # Python operator overloads
330
+ def __add__(self, other):
331
+ cdef Vector2D result
332
+
333
+ if isinstance(other, Vector2D):
334
+ return self.add(other)
335
+ elif isinstance(other, (int, float)):
336
+ result = self.copy()
337
+ result.iadd_scalar(other)
338
+ return result
339
+ return NotImplemented
340
+
341
+ def __sub__(self, other):
342
+ cdef Vector2D result
343
+
344
+ if isinstance(other, Vector2D):
345
+ return self.sub(other)
346
+ elif isinstance(other, (int, float)):
347
+ result = self.copy()
348
+ result.isub_scalar(other)
349
+ return result
350
+ return NotImplemented
351
+
352
+ def __mul__(self, other):
353
+ if isinstance(other, (int, float)):
354
+ return self.mul(other)
355
+ elif isinstance(other, Vector2D):
356
+ return self.mul_vec(other)
357
+ return NotImplemented
358
+
359
+ def __truediv__(self, other):
360
+ cdef Vector2D result
361
+
362
+ if isinstance(other, (int, float)):
363
+ if other != 0.0:
364
+ result = self.copy()
365
+ result.idiv(other)
366
+ return result
367
+ return NotImplemented
368
+
369
+ def __iadd__(self, other):
370
+ if isinstance(other, Vector2D):
371
+ self.iadd(other)
372
+ elif isinstance(other, (int, float)):
373
+ self.iadd_scalar(other)
374
+ return self
375
+
376
+ def __isub__(self, other):
377
+ if isinstance(other, Vector2D):
378
+ self.isub(other)
379
+ elif isinstance(other, (int, float)):
380
+ self.isub_scalar(other)
381
+ return self
382
+
383
+ def __imul__(self, other):
384
+ if isinstance(other, (int, float)):
385
+ self.imul(other)
386
+ elif isinstance(other, Vector2D):
387
+ self.imul_vec(other)
388
+ return self
389
+
390
+ def __itruediv__(self, other):
391
+ if isinstance(other, (int, float)) and other != 0.0:
392
+ self.idiv(other)
393
+ return self
394
+
395
+ def __neg__(self):
396
+ return self.mul(-1.0)
397
+
398
+ def __abs__(self):
399
+ cdef Vector2D result = Vector2D.__new__(Vector2D)
400
+ result.data = np.empty(2, dtype=np.float64)
401
+ result._data_ptr = <DTYPE_t*> cnp.PyArray_DATA(result.data)
402
+ result._data_ptr[0] = fabs(self._data_ptr[0])
403
+ result._data_ptr[1] = fabs(self._data_ptr[1])
404
+ return result
405
+
406
+ def __getitem__(self, int idx):
407
+ if idx == 0:
408
+ return self._data_ptr[0]
409
+ elif idx == 1:
410
+ return self._data_ptr[1]
411
+ raise IndexError("Index out of range")
412
+
413
+ def __setitem__(self, int idx, double value):
414
+ if idx == 0:
415
+ self._data_ptr[0] = value
416
+ elif idx == 1:
417
+ self._data_ptr[1] = value
418
+ else:
419
+ raise IndexError("Index out of range")
420
+
421
+ def __str__(self):
422
+ return f"Vector2D({self._data_ptr[0]:.6f}, {self._data_ptr[1]:.6f})"
423
+
424
+ def __repr__(self):
425
+ return f"Vector2D({self._data_ptr[0]}, {self._data_ptr[1]})"
426
+
427
+ # Utility methods
428
+ cpdef list to_list(self):
429
+ """Convert to Python list"""
430
+ return [self._data_ptr[0], self._data_ptr[1]]
431
+
432
+ cpdef tuple to_tuple(self):
433
+ """Convert to Python tuple"""
434
+ return (self._data_ptr[0], self._data_ptr[1])
435
+
436
+ # Class methods for common vectors
437
+ @staticmethod
438
+ def zero():
439
+ return Vector2D(0.0, 0.0)
440
+
441
+ @staticmethod
442
+ def one():
443
+ return Vector2D(1.0, 1.0)
444
+
445
+ @staticmethod
446
+ def up():
447
+ return Vector2D(0.0, 1.0)
448
+
449
+ @staticmethod
450
+ def down():
451
+ return Vector2D(0.0, -1.0)
452
+
453
+ @staticmethod
454
+ def left():
455
+ return Vector2D(-1.0, 0.0)
456
+
457
+ @staticmethod
458
+ def right():
459
+ return Vector2D(1.0, 0.0)
460
+
461
+ @staticmethod
462
+ def random(double min_val=0.0, double max_val=1.0):
463
+ """Create random vector"""
464
+ cdef double range_val
465
+ cdef double rx
466
+ cdef double ry
467
+
468
+ range_val = max_val - min_val
469
+ rx = (<double>rand() / <double>RAND_MAX) * range_val + min_val
470
+ ry = (<double>rand() / <double>RAND_MAX) * range_val + min_val
471
+ return Vector2D(rx, ry)
472
+
473
+
474
+ # Batch operations for processing many vectors at once
475
+ @cython.boundscheck(False)
476
+ @cython.wraparound(False)
477
+ cpdef void batch_add_inplace(list vectors, Vector2D displacement):
478
+ """Add displacement to all vectors in-place (ultra-fast)"""
479
+ cdef Vector2D vec
480
+ cdef Py_ssize_t i
481
+ cdef Py_ssize_t n = len(vectors)
482
+
483
+ for i in range(n):
484
+ vec = <Vector2D>vectors[i]
485
+ vec._data_ptr[0] += displacement._data_ptr[0]
486
+ vec._data_ptr[1] += displacement._data_ptr[1]
487
+
488
+
489
+ @cython.boundscheck(False)
490
+ @cython.wraparound(False)
491
+ cpdef void batch_scale_inplace(list vectors, double scalar):
492
+ """Scale all vectors in-place"""
493
+ cdef Vector2D vec
494
+ cdef Py_ssize_t i
495
+ cdef Py_ssize_t n = len(vectors)
496
+
497
+ for i in range(n):
498
+ vec = <Vector2D>vectors[i]
499
+ vec._data_ptr[0] *= scalar
500
+ vec._data_ptr[1] *= scalar
501
+
502
+
503
+ @cython.boundscheck(False)
504
+ @cython.wraparound(False)
505
+ cpdef void batch_normalize_inplace(list vectors):
506
+ """Normalize all vectors in-place"""
507
+ cdef Vector2D vec
508
+ cdef Py_ssize_t i
509
+ cdef Py_ssize_t n
510
+ cdef double length
511
+ cdef double inv_length
512
+
513
+ n = len(vectors)
514
+
515
+ for i in range(n):
516
+ vec = <Vector2D>vectors[i]
517
+ length = sqrt(vec._data_ptr[0] * vec._data_ptr[0] + vec._data_ptr[1] * vec._data_ptr[1])
518
+ if length > 0.0:
519
+ inv_length = 1.0 / length
520
+ vec._data_ptr[0] *= inv_length
521
+ vec._data_ptr[1] *= inv_length
522
+
523
+
524
+ @cython.boundscheck(False)
525
+ @cython.wraparound(False)
526
+ cpdef cnp.ndarray[DTYPE_t, ndim=2] vectors_to_array(list vectors):
527
+ """Convert list of vectors to numpy array (fast)"""
528
+ cdef Py_ssize_t n
529
+ cdef cnp.ndarray[DTYPE_t, ndim=2] result
530
+ cdef Vector2D vec
531
+ cdef Py_ssize_t i
532
+
533
+ n = len(vectors)
534
+ result = np.empty((n, 2), dtype=np.float64)
535
+
536
+ for i in range(n):
537
+ vec = <Vector2D>vectors[i]
538
+ result[i, 0] = vec._data_ptr[0]
539
+ result[i, 1] = vec._data_ptr[1]
540
+
541
+ return result
542
+
543
+
544
+ @cython.boundscheck(False)
545
+ @cython.wraparound(False)
546
+ cpdef list array_to_vectors(cnp.ndarray[DTYPE_t, ndim=2] arr):
547
+ """Convert numpy array to list of vectors (fast)"""
548
+ cdef Py_ssize_t n
549
+ cdef list result
550
+ cdef Vector2D vec
551
+ cdef Py_ssize_t i
552
+
553
+ n = arr.shape[0]
554
+ result = []
555
+
556
+ for i in range(n):
557
+ vec = Vector2D(arr[i, 0], arr[i, 1])
558
+ result.append(vec)
559
+
560
+ return result
561
+
e2D/devices.py ADDED
@@ -0,0 +1,74 @@
1
+ from enum import Enum
2
+ import glfw
3
+
4
+ class KeyState(Enum):
5
+ PRESSED = 1
6
+ JUST_PRESSED = 2
7
+ JUST_RELEASED = 3
8
+
9
+ class Keyboard:
10
+ def __init__(self) -> None:
11
+ self.pressed = set()
12
+ self.just_pressed = set()
13
+ self.just_released = set()
14
+
15
+ def _on_key(self, window, key, scancode, action, mods) -> None:
16
+ if action == glfw.PRESS:
17
+ self.pressed.add(key)
18
+ self.just_pressed.add(key)
19
+ elif action == glfw.RELEASE:
20
+ self.pressed.discard(key)
21
+ self.just_released.add(key)
22
+
23
+ def update(self) -> None:
24
+ self.just_pressed.clear()
25
+ self.just_released.clear()
26
+
27
+ def get_key(self, key, state: KeyState) -> bool:
28
+ if state == KeyState.PRESSED:
29
+ return key in self.pressed
30
+ elif state == KeyState.JUST_PRESSED:
31
+ return key in self.just_pressed
32
+ elif state == KeyState.JUST_RELEASED:
33
+ return key in self.just_released
34
+ return False
35
+
36
+ class Mouse:
37
+ def __init__(self) -> None:
38
+ self.position = (0, 0)
39
+ self.last_position = (0, 0)
40
+ self.delta = (0, 0)
41
+ self.scroll = (0, 0)
42
+ self.pressed = set()
43
+ self.just_pressed = set()
44
+ self.just_released = set()
45
+
46
+ def _on_cursor_pos(self, window, x, y) -> None:
47
+ self.position = (x, y)
48
+
49
+ def _on_mouse_button(self, window, button, action, mods) -> None:
50
+ if action == glfw.PRESS:
51
+ self.pressed.add(button)
52
+ self.just_pressed.add(button)
53
+ elif action == glfw.RELEASE:
54
+ self.pressed.discard(button)
55
+ self.just_released.add(button)
56
+
57
+ def _on_scroll(self, window, xoffset, yoffset) -> None:
58
+ self.scroll = (xoffset, yoffset)
59
+
60
+ def update(self) -> None:
61
+ self.delta = (self.position[0] - self.last_position[0], self.position[1] - self.last_position[1])
62
+ self.last_position = self.position
63
+ self.just_pressed.clear()
64
+ self.just_released.clear()
65
+ self.scroll = (0, 0)
66
+
67
+ def get_button(self, button, state: KeyState) -> bool:
68
+ if state == KeyState.PRESSED:
69
+ return button in self.pressed
70
+ elif state == KeyState.JUST_PRESSED:
71
+ return button in self.just_pressed
72
+ elif state == KeyState.JUST_RELEASED:
73
+ return button in self.just_released
74
+ return False