ncca-ngl 0.3.5__py3-none-any.whl → 0.5.1__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.
Files changed (56) hide show
  1. ncca/ngl/PrimData/pack_arrays.py +2 -3
  2. ncca/ngl/__init__.py +5 -5
  3. ncca/ngl/base_mesh.py +28 -20
  4. ncca/ngl/image.py +1 -3
  5. ncca/ngl/mat2.py +79 -53
  6. ncca/ngl/mat3.py +104 -185
  7. ncca/ngl/mat4.py +144 -309
  8. ncca/ngl/prim_data.py +42 -36
  9. ncca/ngl/primitives.py +2 -2
  10. ncca/ngl/pyside_event_handling_mixin.py +0 -108
  11. ncca/ngl/quaternion.py +69 -36
  12. ncca/ngl/shader.py +0 -116
  13. ncca/ngl/shader_program.py +94 -117
  14. ncca/ngl/texture.py +5 -2
  15. ncca/ngl/util.py +50 -0
  16. ncca/ngl/vec2.py +59 -302
  17. ncca/ngl/vec2_array.py +79 -28
  18. ncca/ngl/vec3.py +60 -350
  19. ncca/ngl/vec3_array.py +76 -23
  20. ncca/ngl/vec4.py +90 -200
  21. ncca/ngl/vec4_array.py +78 -27
  22. ncca/ngl/vector_base.py +548 -0
  23. ncca/ngl/webgpu/__init__.py +20 -0
  24. ncca/ngl/webgpu/__main__.py +640 -0
  25. ncca/ngl/webgpu/__main__.py.backup +640 -0
  26. ncca/ngl/webgpu/base_webgpu_pipeline.py +354 -0
  27. ncca/ngl/webgpu/custom_shader_pipeline.py +288 -0
  28. ncca/ngl/webgpu/instanced_geometry_pipeline.py +594 -0
  29. ncca/ngl/webgpu/line_pipeline.py +405 -0
  30. ncca/ngl/webgpu/pipeline_factory.py +190 -0
  31. ncca/ngl/webgpu/pipeline_shaders.py +497 -0
  32. ncca/ngl/webgpu/point_list_pipeline.py +349 -0
  33. ncca/ngl/webgpu/point_pipeline.py +336 -0
  34. ncca/ngl/webgpu/triangle_pipeline.py +419 -0
  35. ncca/ngl/webgpu/webgpu_constants.py +33 -0
  36. ncca/ngl/webgpu/webgpu_widget.py +322 -0
  37. ncca/ngl/webgpu/wip/REFACTORING_SUMMARY.md +82 -0
  38. ncca/ngl/webgpu/wip/UNIFIED_SYSTEM.md +314 -0
  39. ncca/ngl/webgpu/wip/buffer_manager.py +396 -0
  40. ncca/ngl/webgpu/wip/pipeline_config.py +463 -0
  41. ncca/ngl/webgpu/wip/shader_constants.py +328 -0
  42. ncca/ngl/webgpu/wip/shader_templates.py +563 -0
  43. ncca/ngl/webgpu/wip/unified_examples.py +390 -0
  44. ncca/ngl/webgpu/wip/unified_factory.py +449 -0
  45. ncca/ngl/webgpu/wip/unified_pipeline.py +469 -0
  46. ncca/ngl/widgets/__init__.py +18 -2
  47. ncca/ngl/widgets/__main__.py +2 -1
  48. ncca/ngl/widgets/lookatwidget.py +2 -1
  49. ncca/ngl/widgets/mat4widget.py +2 -2
  50. ncca/ngl/widgets/vec2widget.py +1 -1
  51. ncca/ngl/widgets/vec3widget.py +1 -0
  52. {ncca_ngl-0.3.5.dist-info → ncca_ngl-0.5.1.dist-info}/METADATA +3 -2
  53. ncca_ngl-0.5.1.dist-info/RECORD +105 -0
  54. ncca/ngl/widgets/transformation_widget.py +0 -299
  55. ncca_ngl-0.3.5.dist-info/RECORD +0 -82
  56. {ncca_ngl-0.3.5.dist-info → ncca_ngl-0.5.1.dist-info}/WHEEL +0 -0
ncca/ngl/vec2.py CHANGED
@@ -1,360 +1,117 @@
1
1
  """
2
2
  Simple float only Vec2 class for 3D graphics, very similar to the pyngl ones
3
+ NumPy-based implementation with VectorBase inheritance for code reuse.
3
4
  """
4
5
 
5
- import ctypes
6
6
  import math
7
+ from typing import Union
7
8
 
8
9
  import numpy as np
9
10
 
10
- from .util import clamp, hash_combine
11
+ from .util import clamp
12
+ from .vector_base import VectorBase, _create_properties
11
13
 
12
14
 
13
- class Vec2:
15
+ class Vec2(VectorBase["Vec2"]):
14
16
  """
15
- A simple 3D vector class for 3D graphics, I use slots to fix the attributes to x,y,z
17
+ A simple 2D vector class for graphics, using numpy for efficient operations.
18
+
16
19
  Attributes:
17
20
  x (float): The x-coordinate of the vector.
18
21
  y (float): The y-coordinate of the vector.
19
22
  """
20
23
 
21
- __slots__ = ["_x", "_y"] # fix the attributes to x,y,z
22
-
23
- def __init__(self, x=0.0, y=0.0):
24
- """
25
- Initializes a new instance of the Vec2 class.
26
-
27
- Args:
28
- x (float, optional): The x-coordinate of the vector. Defaults to 0.0.
29
- y (float, optional): The y-coordinate of the vector. Defaults to 0.0.
30
- """
31
- self._x = x # x component of vector : float
32
- self._y = y # y component of vector : float
24
+ DIMENSION = 2
25
+ COMPONENT_NAMES = ("x", "y")
26
+ DEFAULT_VALUES = (0.0, 0.0)
33
27
 
34
- @classmethod
35
- def sizeof(cls):
36
- return 2 * ctypes.sizeof(ctypes.c_float)
28
+ __slots__ = ["_data"]
37
29
 
38
- def __iter__(self):
39
- """
40
- Make the Vec2 class iterable.
41
- Yields:
42
- float: The x and y components of the vector.
43
- """
44
- yield self.x
45
- yield self.y
46
-
47
- def __hash__(self):
48
- # Use 32-bit float element hashes, then combine
49
- seed = 0
50
- for v in (self.x, self.y):
51
- # ensure 32-bit float semantics
52
- h = hash(float(np.float32(v)))
53
- seed = hash_combine(seed, h)
54
- return seed
55
-
56
- def copy(self) -> "Vec2":
57
- """
58
- Create a copy of the vector.
59
- Returns:
60
- Vec2: A new instance of Vec2 with the same x and y values.
61
- """
62
- return Vec2(self.x, self.y)
63
-
64
- def __getitem__(self, index):
65
- """
66
- Get the component of the vector at the given index.
67
- Args:
68
- index (int): The index of the component (0 for x, 1 for y, 2 for z).
69
- Returns:
70
- float: The value of the component at the given index.
71
- Raises:
72
- IndexError: If the index is out of range.
30
+ def cross(self, rhs: "Vec2") -> float:
73
31
  """
74
- components = [self.x, self.y]
75
- try:
76
- return components[index]
77
- except IndexError:
78
- raise IndexError("Index out of range. Valid indices are 0, 1,")
32
+ Cross product of two vectors a x b (2D version returns scalar).
79
33
 
80
- def _validate_and_set(self, v, name):
81
- """
82
- check if v is a float or int
83
34
  Args:
84
- v (number): The value to check.
85
- Raises:
86
- ValueError: If v is not a float or int.
87
- """
88
- if not isinstance(v, (int, float, np.float32)):
89
- raise ValueError("need float or int")
90
- else:
91
- setattr(self, name, v)
92
-
93
- def __add__(self, rhs):
94
- """
95
- vector addition a+b
35
+ rhs (Vec2): The right-hand side vector to cross product with.
96
36
 
97
- Args:
98
- rhs (Vec2): The right-hand side vector to add.
99
37
  Returns:
100
- Vec2: A new vector that is the result of adding this vector and the rhs vector.
38
+ float: 2D cross product (perpendicular dot product).
101
39
  """
102
- r = Vec2()
103
- r.x = self.x + rhs.x
104
- r.y = self.y + rhs.y
105
- return r
40
+ return self._data[0] * rhs._data[1] - self._data[1] * rhs._data[0]
106
41
 
107
- def __iadd__(self, rhs):
42
+ def reflect(self, n: "Vec2") -> "Vec2":
108
43
  """
109
- vector addition a+=b
44
+ Reflect a vector about a normal.
110
45
 
111
46
  Args:
112
- rhs (Vec2): The right-hand side vector to add.
113
- Returns:
114
- Vec2: returns this vector after adding the rhs vector.
115
- """
116
- self.x += rhs.x
117
- self.y += rhs.y
118
- return self
119
-
120
- def __sub__(self, rhs):
121
- """
122
- vector subtraction a-b
47
+ n (Vec2): The normal to reflect about.
123
48
 
124
- Args:
125
- rhs (Vec2): The right-hand side vector to add.
126
49
  Returns:
127
- Vec2: A new vector that is the result of subtracting this vector and the rhs vector.
50
+ Vec2: A new vector that is the result of reflecting this vector about the normal.
128
51
  """
129
- r = Vec2()
130
- r.x = self.x - rhs.x
131
- r.y = self.y - rhs.y
132
- return r
52
+ d = self.dot(n)
53
+ # I - 2.0 * dot(N, I) * N
54
+ return Vec2(
55
+ self._data[0] - 2.0 * d * n._data[0], self._data[1] - 2.0 * d * n._data[1]
56
+ )
133
57
 
134
- def __isub__(self, rhs):
58
+ def outer(self, rhs: "Vec2"):
135
59
  """
136
- vector subtraction a-=b
60
+ Outer product of two vectors a x b.
137
61
 
138
62
  Args:
139
- rhs (Vec2): The right-hand side vector to add.
140
- Returns:
141
- Vec2: returns this vector after subtracting the rhs vector.
142
- """
143
- self.x -= rhs.x
144
- self.y -= rhs.y
145
- return self
63
+ rhs (Vec2): The right-hand side vector to outer product with.
146
64
 
147
- def __eq__(self, rhs):
148
- """
149
- vector comparison a==b using math.isclose not we only compare to 6 decimal places
150
- Args:
151
- rhs (Vec2): The right-hand side vector to compare.
152
65
  Returns:
153
- bool: True if the vectors are close, False otherwise.
154
- NotImplemented: If the right-hand side is not a Vec2.
66
+ Mat2: A new 2x2 matrix that is the result of the outer product.
155
67
  """
68
+ from .mat2 import Mat2
156
69
 
157
- if not isinstance(rhs, Vec2):
158
- return NotImplemented
159
- return math.isclose(self.x, rhs.x) and math.isclose(self.y, rhs.y)
160
-
161
- def __neq__(self, rhs):
162
- """
163
- vector comparison a!=b using math.isclose not we only compare to 6 decimal places
164
- Args:
165
- rhs (Vec2): The right-hand side vector to compare.
166
- Returns:
167
- bool: True if the vectors are not close, False otherwise.
168
- NotImplemented: If the right-hand side is not a Vec2.
169
- """
70
+ result = Mat2()
71
+ result.m = np.outer(self._data, rhs._data).astype(np.float64)
72
+ return result
170
73
 
171
- if not isinstance(rhs, Vec2):
172
- return NotImplemented
173
- return not (math.isclose(self.x, rhs.x) and math.isclose(self.y, rhs.y))
174
-
175
- def __neg__(self):
176
- """
177
- negate a vector -a
178
- """
179
- self.x = -self.x
180
- self.y = -self.y
181
- return self
182
-
183
- def set(self, x, y):
184
- """
185
- set the x,y,z values of the vector
186
- Args:
187
- x (float): The x-coordinate of the vector.
188
- y (float): The y-coordinate of the vector.
189
- Raises :
190
- ValueError: if x,y are not float
74
+ def __matmul__(self, rhs):
191
75
  """
192
- try:
193
- self.x = float(x)
194
- self.y = float(y)
195
- except ValueError:
196
- raise ValueError(f"Vec2.set {x=} {y=} all need to be float")
76
+ Vec2 @ Mat2 matrix multiplication.
197
77
 
198
- def dot(self, rhs):
199
- """
200
- dot product of two vectors a.b
201
78
  Args:
202
- rhs (Vec2): The right-hand side vector to dot product with.
203
- """
204
- return self.x * rhs.x + self.y * rhs.y
205
-
206
- def length(self):
207
- """
208
- length of vector
209
- Returns:
210
- float: The length of the vector.
211
- """
212
- return math.sqrt(self.x**2 + self.y**2)
213
-
214
- def length_squared(self):
215
- """
216
- length of vector squared sometimes used to avoid the sqrt for performance
217
- Returns:
218
- float: The length of the vector squared
219
- """
220
- return self.x**2 + self.y**2
79
+ rhs (Mat2): The matrix to multiply by.
221
80
 
222
- def inner(self, rhs):
223
- """
224
- inner product of two vectors a.b
225
- Args:
226
- rhs (Vec2): The right-hand side vector to inner product with.
227
81
  Returns:
228
- float: The inner product of the two vectors.
82
+ Vec2: A new vector that is the result of multiplying this vector by the matrix.
229
83
  """
230
- return (self.x * rhs.x) + (self.y * rhs.y)
84
+ return Vec2(
85
+ self._data[0] * rhs.m[0, 0] + self._data[1] * rhs.m[1, 0],
86
+ self._data[0] * rhs.m[0, 1] + self._data[1] * rhs.m[1, 1],
87
+ )
231
88
 
232
- def null(self):
233
- """
234
- set the vector to zero
89
+ def set(self, *args: float) -> None:
235
90
  """
236
- self.x = 0.0
237
- self.y = 0.0
91
+ Set the x,y values of the vector.
238
92
 
239
- def cross(self, rhs):
240
- """
241
- cross product of two vectors a x b
242
93
  Args:
243
- rhs (Vec2): The right-hand side vector to cross product with.
244
- Returns:
245
- float : 2D cross product or perpendicular dot product.
246
- """
247
- return self.x * rhs.y - self.y * rhs.x
94
+ *args: Component values (x, y).
248
95
 
249
- def normalize(self):
250
- """
251
- normalize the vector to unit length
252
- Returns:
253
- Vec2: A new vector that is the result of normalizing this vector.
254
96
  Raises:
255
- ZeroDivisionError: If the length of the vector is zero.
97
+ ValueError: If wrong number of arguments or they are not floats.
256
98
  """
257
- vector_length = self.length()
99
+ if len(args) != 2:
100
+ raise ValueError(f"Vec2.set requires 2 arguments, got {len(args)}")
258
101
  try:
259
- self.x /= vector_length
260
- self.y /= vector_length
261
- except ZeroDivisionError:
262
- raise ZeroDivisionError(
263
- f"Vec2.normalize {vector_length} length is zero most likely calling normalize on a zero vector"
264
- )
265
- return self
266
-
267
- def reflect(self, n):
268
- """
269
- reflect a vector about a normal
270
- Args:
271
- n (Vec2): The normal to reflect about.
272
- Returns:
273
- Vec2: A new vector that is the result of reflecting this vector about the normal.
274
- """
275
- d = self.dot(n)
276
- # I - 2.0 * dot(N, I) * N
277
- return Vec2(self.x - 2.0 * d * n.x, self.y - 2.0 * d * n.y)
278
-
279
- def clamp(self, low, high):
280
- """
281
- clamp the vector to a range
282
- Args:
283
- low (float): The low end of the range.
284
- high (float): The high end of the range.
285
-
286
- """
287
- self.x = clamp(self.x, low, high)
288
- self.y = clamp(self.y, low, high)
289
-
290
- def __repr__(self):
291
- "object representation for debugging"
292
- return f"Vec2 [{self.x},{self.y}]"
293
-
294
- def __truediv__(self, rhs):
295
- if isinstance(rhs, (float, int)):
296
- return Vec2(self.x / rhs, self.y / rhs)
297
- elif isinstance(rhs, Vec2):
298
- return Vec2(self.x / rhs.x, self.y / rhs.y)
299
- else:
300
- raise ValueError(f"can only do piecewise division with a scalar {rhs=}")
301
-
302
- def __str__(self):
303
- "object representation for debugging"
304
- return f"[{self.x},{self.y}]"
305
-
306
- def __mul__(self, rhs):
307
- """
308
- piecewise scalar multiplication
309
- Args:
310
- rhs (float): The scalar to multiply by.
311
- Returns:
312
- Vec2: A new vector that is the result of multiplying this vector by the scalar.
313
- Raises:
314
- ValueError: If the right-hand side is not a float.
315
- """
316
- if isinstance(rhs, (float, int)):
317
- return Vec2(self.x * rhs, self.y * rhs)
318
- else:
319
- raise ValueError(f"can only do piecewise multiplication with a scalar {rhs=}")
320
-
321
- def __rmul__(self, rhs):
322
- """
323
- piecewise scalar multiplication
324
- Args:
325
- rhs (float): The scalar to multiply by.
326
- Returns:
327
- Vec2: A new vector that is the result of multiplying this vector by the scalar.
328
- Raises:
329
- ValueError: If the right-hand side is not a float.
330
- """
331
- return self * rhs
332
-
333
- def __matmul__(self, rhs):
334
- """
335
- "Vec2 @ Mat2 matrix multiplication"
336
- Args:
337
- rhs (Mat2): The matrix to multiply by.
338
- Returns:
339
- Vec2: A new vector that is the result of multiplying this vector by the matrix.
340
- """
341
- return Vec2(
342
- self.x * rhs.m[0][0] + self.y * rhs.m[1][0],
343
- self.x * rhs.m[0][1] + self.y * rhs.m[1][1],
344
- )
345
-
346
-
347
- # Helper function to create properties
348
- def _create_property(attr_name):
349
- def getter(self):
350
- return getattr(self, f"_{attr_name}")
102
+ self._data[0] = float(args[0])
103
+ self._data[1] = float(args[1])
104
+ except ValueError:
105
+ raise ValueError(f"Vec2.set {args=} all need to be float")
351
106
 
352
- def setter(self, value):
353
- self._validate_and_set(value, f"_{attr_name}")
107
+ def __repr__(self) -> str:
108
+ """Object representation for debugging."""
109
+ return f"Vec2 [{self._data[0]},{self._data[1]}]"
354
110
 
355
- return property(getter, setter)
111
+ def __str__(self) -> str:
112
+ """String representation of the vector."""
113
+ return f"[{self._data[0]},{self._data[1]}]"
356
114
 
357
115
 
358
- # Dynamically add properties for x, y
359
- for attr in ["x", "y"]:
360
- setattr(Vec2, attr, _create_property(attr))
116
+ # Add properties for x, y components
117
+ _create_properties(Vec2)
ncca/ngl/vec2_array.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """
2
2
  A container for ngl.Vec2 objects that mimics some of the behavior of a std::vector
3
+ Optimized for graphics APIs with contiguous numpy storage
3
4
  """
4
5
 
5
6
  import numpy as np
@@ -9,40 +10,55 @@ from .vec2 import Vec2
9
10
 
10
11
  class Vec2Array:
11
12
  """
12
- A class to hold a list of Vec2 objects and perform operations on them.
13
+ A class to hold Vec2 data in contiguous memory for efficient GPU transfer.
14
+ Internally uses a numpy array of shape (N, 2) for optimal performance.
13
15
  """
14
16
 
15
17
  def __init__(self, values=None):
16
18
  """
17
- Initializes the Vec3Array.
19
+ Initializes the Vec2Array.
18
20
 
19
21
  Args:
20
- values (iterable | int, optional): An iterable of Vec3 objects or an integer.
21
- If an integer, the array is initialized with that many default Vec3s.
22
- If an iterable, it's initialized with the Vec3s from the iterable.
22
+ values (iterable | int, optional): An iterable of Vec2 objects or an integer.
23
+ If an integer, the array is initialized with that many default Vec2s.
24
+ If an iterable, it's initialized with the Vec2s from the iterable.
23
25
  Defaults to None (an empty array).
24
26
  """
25
- self._data = []
26
- if values is not None:
27
- if isinstance(values, int):
28
- self._data = [Vec2() for _ in range(values)]
29
- else:
30
- for v in values:
31
- if not isinstance(v, Vec2):
32
- raise TypeError("All elements must be of type Vec2")
33
- self._data.append(v)
27
+ if values is None:
28
+ # Empty array - start with shape (0, 2)
29
+ self._data = np.zeros((0, 2), dtype=np.float64)
30
+ elif isinstance(values, int):
31
+ # Initialize N default Vec2s (0, 0)
32
+ self._data = np.zeros((values, 2), dtype=np.float64)
33
+ else:
34
+ # Initialize from iterable of Vec2 objects
35
+ vec_list = []
36
+ for v in values:
37
+ if not isinstance(v, Vec2):
38
+ raise TypeError("All elements must be of type Vec2")
39
+ vec_list.append([v.x, v.y])
40
+ self._data = np.array(vec_list, dtype=np.float64)
34
41
 
35
42
  def __getitem__(self, index):
36
43
  """
37
44
  Get the Vec2 at the specified index.
38
45
 
39
46
  Args:
40
- index (int): The index of the element.
47
+ index (int | slice): The index or slice of the element(s).
41
48
 
42
49
  Returns:
43
50
  Vec2: The Vec2 object at the given index.
44
- """
45
- return self._data[index]
51
+ Vec2Array: A new Vec2Array if slicing.
52
+ """
53
+ if isinstance(index, slice):
54
+ # Return a new Vec2Array with sliced data
55
+ result = Vec2Array()
56
+ result._data = self._data[index].copy()
57
+ return result
58
+ else:
59
+ # Return a single Vec2
60
+ row = self._data[index]
61
+ return Vec2(row[0], row[1])
46
62
 
47
63
  def __setitem__(self, index, value):
48
64
  """
@@ -50,11 +66,11 @@ class Vec2Array:
50
66
 
51
67
  Args:
52
68
  index (int): The index of the element to set.
53
- value (Vec3): The new Vec3 object.
69
+ value (Vec2): The new Vec2 object.
54
70
  """
55
71
  if not isinstance(value, Vec2):
56
72
  raise TypeError("Only Vec2 objects can be assigned")
57
- self._data[index] = value
73
+ self._data[index] = [value.x, value.y]
58
74
 
59
75
  def __len__(self):
60
76
  """
@@ -64,9 +80,25 @@ class Vec2Array:
64
80
 
65
81
  def __iter__(self):
66
82
  """
67
- Return an iterator for the array.
83
+ Return an iterator that yields Vec2 objects.
84
+ """
85
+ for i in range(len(self._data)):
86
+ row = self._data[i]
87
+ yield Vec2(row[0], row[1])
88
+
89
+ def __eq__(self, other):
90
+ """
91
+ Compare two Vec2Array instances for equality.
92
+
93
+ Args:
94
+ other: Another Vec2Array instance to compare with.
95
+
96
+ Returns:
97
+ bool: True if the arrays contain the same data, False otherwise.
68
98
  """
69
- return iter(self._data)
99
+ if not isinstance(other, Vec2Array):
100
+ return NotImplemented
101
+ return np.array_equal(self._data, other._data)
70
102
 
71
103
  def append(self, value):
72
104
  """
@@ -77,7 +109,8 @@ class Vec2Array:
77
109
  """
78
110
  if not isinstance(value, Vec2):
79
111
  raise TypeError("Only Vec2 objects can be appended")
80
- self._data.append(value)
112
+ new_row = np.array([[value.x, value.y]], dtype=np.float64)
113
+ self._data = np.vstack([self._data, new_row])
81
114
 
82
115
  def extend(self, values):
83
116
  """
@@ -88,7 +121,12 @@ class Vec2Array:
88
121
  """
89
122
  if not all(isinstance(v, Vec2) for v in values):
90
123
  raise TypeError("All elements must be of type Vec2")
91
- self._data.extend(values)
124
+
125
+ new_rows = np.array([[v.x, v.y] for v in values], dtype=np.float64)
126
+ if len(self._data) == 0:
127
+ self._data = new_rows
128
+ else:
129
+ self._data = np.vstack([self._data, new_rows])
92
130
 
93
131
  def to_list(self):
94
132
  """
@@ -97,22 +135,35 @@ class Vec2Array:
97
135
  Returns:
98
136
  list: A list of x, y components concatenated.
99
137
  """
100
- return [comp for vec in self._data for comp in vec]
138
+ return self._data.flatten().tolist()
101
139
 
102
140
  def to_numpy(self):
103
141
  """
104
142
  Convert the array of Vec2 objects to a numpy array.
143
+ This is the primary method for GPU data transfer.
144
+
145
+ Returns:
146
+ numpy.ndarray: A float32 numpy array of shape (N*2,) for GPU transfer.
147
+ """
148
+ return self._data.astype(np.float32).flatten()
149
+
150
+ def get_array(self):
151
+ """
152
+ Get the underlying numpy array in shape (N, 2).
153
+ Useful for vectorized operations.
105
154
 
106
155
  Returns:
107
- numpy.ndarray: A numpy array of the vector data.
156
+ numpy.ndarray: The internal float64 array of shape (N, 2).
108
157
  """
109
- return np.array(self.to_list(), dtype=np.float32)
158
+ return self._data
110
159
 
111
160
  def __repr__(self):
112
- return f"Vec2Array({self._data!r})"
161
+ vec_list = [Vec2(row[0], row[1]) for row in self._data]
162
+ return f"Vec2Array({vec_list!r})"
113
163
 
114
164
  def __str__(self):
115
- return str(self._data)
165
+ vec_list = [Vec2(row[0], row[1]) for row in self._data]
166
+ return str(vec_list)
116
167
 
117
168
  def sizeof(self):
118
169
  """