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