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/mat3.py
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Simple Mat3 class which can be used with the Vec3 class. By default it will be set to the identity matrix
|
|
3
|
-
|
|
4
|
-
all matrix values are stored in a 3x3 list in the format
|
|
5
|
-
|
|
6
|
-
.. highlight:: python
|
|
7
|
-
.. code-block:: python
|
|
8
|
-
|
|
9
|
-
m=[[1.0,0.0,0.0],
|
|
10
|
-
[0.0,1.0,0.0],
|
|
11
|
-
[0.0,1.0,0.0]]
|
|
12
|
-
|
|
3
|
+
NumPy-based implementation
|
|
13
4
|
"""
|
|
14
5
|
|
|
15
6
|
import copy
|
|
@@ -32,9 +23,6 @@ class Mat3NotSquare(Exception):
|
|
|
32
23
|
pass
|
|
33
24
|
|
|
34
25
|
|
|
35
|
-
_identity = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
|
|
36
|
-
|
|
37
|
-
|
|
38
26
|
class Mat3:
|
|
39
27
|
"""Simple Mat3 class for basic affine transforms"""
|
|
40
28
|
|
|
@@ -45,7 +33,7 @@ class Mat3:
|
|
|
45
33
|
|
|
46
34
|
def __init__(self):
|
|
47
35
|
"""construct to identity matrix"""
|
|
48
|
-
self.m =
|
|
36
|
+
self.m = np.eye(3, dtype=np.float64)
|
|
49
37
|
|
|
50
38
|
def get_matrix(self):
|
|
51
39
|
"""return matrix elements as list ideal for OpenGL
|
|
@@ -56,12 +44,11 @@ class Mat3:
|
|
|
56
44
|
the 9 float elements of the matrix, ideal
|
|
57
45
|
for OpenGL or Renderman consumption
|
|
58
46
|
"""
|
|
59
|
-
|
|
60
|
-
return functools.reduce(operator.concat, self.m)
|
|
47
|
+
return self.m.flatten("C").tolist()
|
|
61
48
|
|
|
62
49
|
def to_numpy(self):
|
|
63
50
|
"return matrix as a numpy array ideal for WebGPU etc"
|
|
64
|
-
return
|
|
51
|
+
return self.m.astype(np.float32)
|
|
65
52
|
|
|
66
53
|
@classmethod
|
|
67
54
|
def identity(cls):
|
|
@@ -87,63 +74,35 @@ class Mat3:
|
|
|
87
74
|
|
|
88
75
|
"""
|
|
89
76
|
v = Mat3()
|
|
90
|
-
v.m =
|
|
77
|
+
v.m = np.zeros((3, 3), dtype=np.float64)
|
|
91
78
|
return v
|
|
92
79
|
|
|
93
80
|
@classmethod
|
|
94
81
|
def from_list(cls, lst):
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
Parameters
|
|
98
|
-
----------
|
|
99
|
-
lst : list
|
|
100
|
-
|
|
101
|
-
list of 9 numbers to construct a new Mat3 from, we will accept either 9 floats or 3 lists of size 3
|
|
102
|
-
|
|
103
|
-
.. highlight:: python
|
|
104
|
-
.. code-block:: python
|
|
105
|
-
|
|
106
|
-
a=Mat3.from_list([1,2,3,4,5,6,7,8,9])
|
|
107
|
-
b=Mat3.from_list([1,2,3],[4,5,6],[7,8,9])
|
|
108
|
-
|
|
109
|
-
Returns
|
|
110
|
-
-------
|
|
111
|
-
Mat3
|
|
112
|
-
new Mat3 from list elements
|
|
113
|
-
|
|
114
|
-
:raises:
|
|
115
|
-
Mat3NotSquare : if we don't get a 3x3 we raise this
|
|
116
|
-
|
|
117
|
-
"""
|
|
82
|
+
"class method to create mat3 from list"
|
|
118
83
|
v = Mat3()
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if len(
|
|
122
|
-
v.m =
|
|
84
|
+
if isinstance(lst, list) and len(lst) == 3 and all(isinstance(row, list) for row in lst):
|
|
85
|
+
# 2D list
|
|
86
|
+
if all(len(row) == 3 for row in lst):
|
|
87
|
+
v.m = np.array(lst, dtype=np.float64)
|
|
123
88
|
return v
|
|
124
|
-
|
|
89
|
+
elif any(len(row) != 3 for row in lst):
|
|
125
90
|
raise Mat3NotSquare
|
|
126
|
-
|
|
91
|
+
elif isinstance(lst, list) and len(lst) == 9:
|
|
92
|
+
# flat list - reshape to 3x3 in row-major order
|
|
93
|
+
v.m = np.array(lst, dtype=np.float64).reshape(3, 3, order="C")
|
|
127
94
|
return v
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
"""ensure matrix is square"""
|
|
131
|
-
return len(self.m) == 3 and all(len(i) == 3 for i in self.m)
|
|
95
|
+
else:
|
|
96
|
+
raise Mat3NotSquare
|
|
132
97
|
|
|
133
98
|
def transpose(self):
|
|
134
99
|
"""transpose this matrix"""
|
|
135
|
-
self.m =
|
|
100
|
+
self.m = self.m.T
|
|
136
101
|
|
|
137
102
|
def get_transpose(self):
|
|
138
|
-
"
|
|
139
|
-
|
|
140
|
-
Returns
|
|
141
|
-
-------
|
|
142
|
-
Mat3
|
|
143
|
-
The transpose of the current matrix
|
|
144
|
-
"""
|
|
103
|
+
"return a new matrix as the transpose of ourself"
|
|
145
104
|
m = Mat3()
|
|
146
|
-
m.m =
|
|
105
|
+
m.m = self.m.T.copy()
|
|
147
106
|
return m
|
|
148
107
|
|
|
149
108
|
@classmethod
|
|
@@ -170,11 +129,11 @@ class Mat3:
|
|
|
170
129
|
matrix with diagonals set to the scale
|
|
171
130
|
|
|
172
131
|
"""
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return
|
|
132
|
+
a = Mat3()
|
|
133
|
+
a.m[0, 0] = x
|
|
134
|
+
a.m[1, 1] = y
|
|
135
|
+
a.m[2, 2] = z
|
|
136
|
+
return a
|
|
178
137
|
|
|
179
138
|
@classmethod
|
|
180
139
|
def rotate_x(cls, angle: float):
|
|
@@ -196,15 +155,15 @@ class Mat3:
|
|
|
196
155
|
Mat3
|
|
197
156
|
rotation matrix
|
|
198
157
|
"""
|
|
199
|
-
|
|
158
|
+
a = Mat3()
|
|
200
159
|
beta = math.radians(angle)
|
|
201
160
|
sr = math.sin(beta)
|
|
202
161
|
cr = math.cos(beta)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return
|
|
162
|
+
a.m[1, 1] = cr
|
|
163
|
+
a.m[1, 2] = sr
|
|
164
|
+
a.m[2, 1] = -sr
|
|
165
|
+
a.m[2, 2] = cr
|
|
166
|
+
return a
|
|
208
167
|
|
|
209
168
|
@classmethod
|
|
210
169
|
def rotate_y(cls, angle: float):
|
|
@@ -226,15 +185,15 @@ class Mat3:
|
|
|
226
185
|
Mat3
|
|
227
186
|
rotation matrix
|
|
228
187
|
"""
|
|
229
|
-
|
|
188
|
+
a = Mat3()
|
|
230
189
|
beta = math.radians(angle)
|
|
231
190
|
sr = math.sin(beta)
|
|
232
191
|
cr = math.cos(beta)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return
|
|
192
|
+
a.m[0, 0] = cr
|
|
193
|
+
a.m[0, 2] = -sr
|
|
194
|
+
a.m[2, 0] = sr
|
|
195
|
+
a.m[2, 2] = cr
|
|
196
|
+
return a
|
|
238
197
|
|
|
239
198
|
@classmethod
|
|
240
199
|
def rotate_z(cls, angle: float):
|
|
@@ -256,15 +215,15 @@ class Mat3:
|
|
|
256
215
|
Mat3
|
|
257
216
|
rotation matrix
|
|
258
217
|
"""
|
|
259
|
-
|
|
218
|
+
a = Mat3()
|
|
260
219
|
beta = math.radians(angle)
|
|
261
220
|
sr = math.sin(beta)
|
|
262
221
|
cr = math.cos(beta)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return
|
|
222
|
+
a.m[0, 0] = cr
|
|
223
|
+
a.m[0, 1] = sr
|
|
224
|
+
a.m[1, 0] = -sr
|
|
225
|
+
a.m[1, 1] = cr
|
|
226
|
+
return a
|
|
268
227
|
|
|
269
228
|
def __getitem__(self, idx):
|
|
270
229
|
"""access array elements remember this is a list of lists [[3],[3],[3]]
|
|
@@ -275,7 +234,7 @@ class Mat3:
|
|
|
275
234
|
|
|
276
235
|
|
|
277
236
|
"""
|
|
278
|
-
return self.m[idx]
|
|
237
|
+
return self.m[idx].tolist()
|
|
279
238
|
|
|
280
239
|
def __setitem__(self, idx, item):
|
|
281
240
|
"""set array elements remember this is a list of lists [[3],[3],[3]]
|
|
@@ -299,105 +258,65 @@ class Mat3:
|
|
|
299
258
|
if rhs is not a number
|
|
300
259
|
"""
|
|
301
260
|
if isinstance(rhs, (int, float)):
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
return self
|
|
261
|
+
result = Mat3()
|
|
262
|
+
result.m = self.m * rhs
|
|
263
|
+
return result
|
|
306
264
|
raise Mat3Error
|
|
307
265
|
|
|
308
266
|
def _mat_mul(self, rhs):
|
|
309
|
-
"matrix mult
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
a10 = self.m[1][0]
|
|
315
|
-
a11 = self.m[1][1]
|
|
316
|
-
a12 = self.m[1][2]
|
|
317
|
-
a20 = self.m[2][0]
|
|
318
|
-
a21 = self.m[2][1]
|
|
319
|
-
a22 = self.m[2][2]
|
|
320
|
-
|
|
321
|
-
b00 = rhs.m[0][0]
|
|
322
|
-
b01 = rhs.m[0][1]
|
|
323
|
-
b02 = rhs.m[0][2]
|
|
324
|
-
b10 = rhs.m[1][0]
|
|
325
|
-
b11 = rhs.m[1][1]
|
|
326
|
-
b12 = rhs.m[1][2]
|
|
327
|
-
b20 = rhs.m[2][0]
|
|
328
|
-
b21 = rhs.m[2][1]
|
|
329
|
-
b22 = rhs.m[2][2]
|
|
330
|
-
|
|
331
|
-
ret=Mat3() # result mat4
|
|
332
|
-
ret.m[0][0] = b00 * a00 + b01 * a10 + b02 * a20
|
|
333
|
-
ret.m[0][1] = b00 * a01 + b01 * a11 + b02 * a21
|
|
334
|
-
ret.m[0][2] = b00 * a02 + b01 * a12 + b02 * a22
|
|
335
|
-
ret.m[1][0] = b10 * a00 + b11 * a10 + b12 * a20
|
|
336
|
-
ret.m[1][1] = b10 * a01 + b11 * a11 + b12 * a21
|
|
337
|
-
ret.m[1][2] = b10 * a02 + b11 * a12 + b12 * a22
|
|
338
|
-
ret.m[2][0] = b20 * a00 + b21 * a10 + b22 * a20
|
|
339
|
-
ret.m[2][1] = b20 * a01 + b21 * a11 + b22 * a21
|
|
340
|
-
ret.m[2][2] = b20 * a02 + b21 * a12 + b22 * a22
|
|
341
|
-
return ret
|
|
342
|
-
# fmt: on
|
|
267
|
+
"matrix mult for 3D OpenGL style graphics"
|
|
268
|
+
result = Mat3()
|
|
269
|
+
# Use numpy's @ operator which does standard matrix multiplication
|
|
270
|
+
result.m = rhs.m @ self.m
|
|
271
|
+
return result
|
|
343
272
|
|
|
344
273
|
def __matmul__(self, rhs):
|
|
345
274
|
"multiply matrix by another matrix"
|
|
346
|
-
from .vec3 import Vec3
|
|
275
|
+
from .vec3 import Vec3
|
|
347
276
|
|
|
277
|
+
"multiply matrix by another matrix or vector"
|
|
348
278
|
if isinstance(rhs, Mat3):
|
|
349
279
|
return self._mat_mul(rhs)
|
|
350
280
|
elif isinstance(rhs, Vec3):
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
)
|
|
281
|
+
# Vector transformation
|
|
282
|
+
vec = np.array([rhs.x, rhs.y, rhs.z], dtype=np.float64)
|
|
283
|
+
res = self.m @ vec
|
|
284
|
+
return Vec3(res[0], res[1], res[2])
|
|
356
285
|
else:
|
|
357
286
|
raise Mat3Error
|
|
358
287
|
|
|
359
|
-
def _add(self, rhs):
|
|
360
|
-
"internal add function"
|
|
361
|
-
temp = Mat3()
|
|
362
|
-
for i in range(0, len(temp.m)):
|
|
363
|
-
temp.m[i] = [a + b for a, b in zip(self.m[i], rhs.m[i], strict=False)]
|
|
364
|
-
return temp
|
|
365
|
-
|
|
366
288
|
def __add__(self, rhs):
|
|
367
289
|
"piecewise addition of elements"
|
|
368
|
-
|
|
290
|
+
result = Mat3()
|
|
291
|
+
result.m = self.m + rhs.m
|
|
292
|
+
return result
|
|
369
293
|
|
|
370
294
|
def __iadd__(self, rhs):
|
|
371
295
|
"piecewise addition of elements to this"
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
"internal subtract function"
|
|
376
|
-
temp = Mat3()
|
|
377
|
-
for i in range(0, len(temp.m)):
|
|
378
|
-
temp.m[i] = [a - b for a, b in zip(self.m[i], rhs.m[i], strict=False)]
|
|
379
|
-
return temp
|
|
296
|
+
result = Mat3()
|
|
297
|
+
result.m = self.m + rhs.m
|
|
298
|
+
return result
|
|
380
299
|
|
|
381
300
|
def __sub__(self, rhs):
|
|
382
301
|
"piecewise subtraction of elements"
|
|
383
|
-
|
|
302
|
+
result = Mat3()
|
|
303
|
+
result.m = self.m - rhs.m
|
|
304
|
+
return result
|
|
384
305
|
|
|
385
306
|
def __isub__(self, rhs):
|
|
386
307
|
"piecewise subtraction of elements to this"
|
|
387
|
-
|
|
308
|
+
result = Mat3()
|
|
309
|
+
result.m = self.m - rhs.m
|
|
310
|
+
return result
|
|
388
311
|
|
|
389
312
|
def determinant(self):
|
|
390
313
|
"determinant of matrix"
|
|
391
|
-
return (
|
|
392
|
-
+self.m[0][0] * (self.m[1][1] * self.m[2][2] - self.m[2][1] * self.m[1][2])
|
|
393
|
-
- self.m[0][1] * (self.m[1][0] * self.m[2][2] - self.m[1][2] * self.m[2][0])
|
|
394
|
-
+ self.m[0][2] * (self.m[1][0] * self.m[2][1] - self.m[1][1] * self.m[2][0])
|
|
395
|
-
)
|
|
314
|
+
return np.linalg.det(self.m)
|
|
396
315
|
|
|
397
316
|
def to_list(self):
|
|
398
317
|
"convert matrix to list"
|
|
399
318
|
# flatten to single array
|
|
400
|
-
return
|
|
319
|
+
return self.m.flatten("C").tolist()
|
|
401
320
|
|
|
402
321
|
def copy(self) -> "Mat3":
|
|
403
322
|
"""Create a copy of the matrix.
|
|
@@ -411,46 +330,46 @@ class Mat3:
|
|
|
411
330
|
|
|
412
331
|
def inverse(self):
|
|
413
332
|
"Inverse of matrix raise MatrixError if not calculable"
|
|
414
|
-
det = self.determinant()
|
|
415
333
|
try:
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
tmp.m[1][0] = -(self.m[1][0] * self.m[2][2] - self.m[1][2] * self.m[2][0]) * invdet
|
|
421
|
-
tmp.m[2][0] = +(self.m[1][0] * self.m[2][1] - self.m[1][1] * self.m[2][0]) * invdet
|
|
422
|
-
|
|
423
|
-
tmp.m[0][1] = -(self.m[0][1] * self.m[2][2] - self.m[0][2] * self.m[2][1]) * invdet
|
|
424
|
-
tmp.m[1][1] = +(self.m[0][0] * self.m[2][2] - self.m[0][2] * self.m[2][0]) * invdet
|
|
425
|
-
tmp.m[2][1] = -(self.m[0][0] * self.m[2][1] - self.m[0][1] * self.m[2][0]) * invdet
|
|
426
|
-
|
|
427
|
-
tmp.m[0][2] = +(self.m[0][1] * self.m[1][2] - self.m[0][2] * self.m[1][1]) * invdet
|
|
428
|
-
tmp.m[1][2] = -(self.m[0][0] * self.m[1][2] - self.m[0][2] * self.m[1][0]) * invdet
|
|
429
|
-
tmp.m[2][2] = +(self.m[0][0] * self.m[1][1] - self.m[0][1] * self.m[1][0]) * invdet
|
|
430
|
-
|
|
431
|
-
return tmp
|
|
432
|
-
except ZeroDivisionError:
|
|
334
|
+
result = Mat3()
|
|
335
|
+
result.m = np.linalg.inv(self.m)
|
|
336
|
+
return result
|
|
337
|
+
except np.linalg.LinAlgError:
|
|
433
338
|
raise Mat3Error
|
|
434
339
|
|
|
435
340
|
def __str__(self):
|
|
436
|
-
|
|
437
|
-
return f"[{
|
|
341
|
+
rows = [self.m[i].tolist() for i in range(3)]
|
|
342
|
+
return f"[{rows[0]}\n{rows[1]}\n{rows[2]}]"
|
|
438
343
|
|
|
439
344
|
def __repr__(self):
|
|
440
|
-
|
|
441
|
-
return f"Mat3({
|
|
345
|
+
rows = [self.m[i].tolist() for i in range(3)]
|
|
346
|
+
return f"Mat3({rows})"
|
|
442
347
|
|
|
443
348
|
@classmethod
|
|
444
349
|
def from_mat4(cls, mat4):
|
|
445
350
|
"""Create a Mat3 from a Mat4"""
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
351
|
+
m = Mat3()
|
|
352
|
+
m.m = mat4.m[:3, :3].copy()
|
|
353
|
+
return m
|
|
354
|
+
|
|
355
|
+
def __eq__(self, other):
|
|
356
|
+
"""Value-based equality for Mat3: compare underlying matrices numerically.
|
|
357
|
+
|
|
358
|
+
Returns NotImplemented for non-Mat3 types so Python can try reflected comparisons
|
|
359
|
+
or handle it appropriately.
|
|
360
|
+
"""
|
|
361
|
+
if not isinstance(other, Mat3):
|
|
362
|
+
return NotImplemented
|
|
363
|
+
# self.m and other.m should be numpy arrays; compare with tolerance
|
|
364
|
+
return np.allclose(self.m, other.m, rtol=1e-8, atol=1e-12)
|
|
365
|
+
|
|
366
|
+
def __ne__(self, other):
|
|
367
|
+
"""Value-based inequality for Mat3: compare underlying matrices numerically.
|
|
368
|
+
|
|
369
|
+
Returns NotImplemented for non-Mat3 types so Python can try reflected comparisons
|
|
370
|
+
or handle it appropriately.
|
|
371
|
+
"""
|
|
372
|
+
if not isinstance(other, Mat3):
|
|
373
|
+
return NotImplemented
|
|
374
|
+
# Return the negation of equality
|
|
375
|
+
return not np.allclose(self.m, other.m, rtol=1e-8, atol=1e-12)
|