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.
Files changed (56) hide show
  1. ncca/ngl/PrimData/pack_arrays.py +2 -3
  2. ncca/ngl/__init__.py +3 -4
  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 +7 -0
  16. ncca/ngl/vec2.py +58 -292
  17. ncca/ngl/vec2_array.py +79 -28
  18. ncca/ngl/vec3.py +59 -340
  19. ncca/ngl/vec3_array.py +76 -23
  20. ncca/ngl/vec4.py +90 -190
  21. ncca/ngl/vec4_array.py +78 -27
  22. ncca/ngl/vector_base.py +542 -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 +31 -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.4.dist-info → ncca_ngl-0.5.0.dist-info}/METADATA +3 -2
  53. ncca_ngl-0.5.0.dist-info/RECORD +105 -0
  54. ncca/ngl/widgets/transformation_widget.py +0 -299
  55. ncca_ngl-0.3.4.dist-info/RECORD +0 -82
  56. {ncca_ngl-0.3.4.dist-info → ncca_ngl-0.5.0.dist-info}/WHEEL +0 -0
ncca/ngl/mat4.py CHANGED
@@ -1,49 +1,41 @@
1
1
  """
2
2
  Simple Mat4 class which can be used with the Vec4 class
3
+ NumPy-based implementation
3
4
  """
4
5
 
5
6
  import copy
6
- import functools
7
7
  import math
8
- import operator
9
8
 
10
9
  import numpy as np
11
10
 
12
11
 
13
12
  class Mat4Error(Exception):
14
- """An exception class for Mat3"""
13
+ """An exception class for Mat4"""
15
14
 
16
15
  pass
17
16
 
18
17
 
19
18
  class Mat4NotSquare(Exception):
20
- """Make sure we have 3x3"""
19
+ """Make sure we have 4x4"""
21
20
 
22
21
  pass
23
22
 
24
23
 
25
- _identity = [
26
- [1.0, 0.0, 0.0, 0.0],
27
- [0.0, 1.0, 0.0, 0.0],
28
- [0.0, 0.0, 1.0, 0.0],
29
- [0.0, 0.0, 0.0, 1.0],
30
- ]
31
-
32
-
33
24
  class Mat4:
34
25
  __slots__ = ["m"]
35
26
 
36
27
  def __init__(self):
37
28
  "construct to identity matrix"
38
- self.m = copy.deepcopy(_identity)
29
+ self.m = np.eye(4, dtype=np.float64)
39
30
 
40
31
  def get_matrix(self):
41
32
  "return matrix elements as list ideal for OpenGL etc"
42
- return functools.reduce(operator.concat, self.m)
33
+ # Flatten in row-major order (C-style)
34
+ return self.m.flatten("C").tolist()
43
35
 
44
36
  def to_numpy(self):
45
37
  "return matrix as a numpy array ideal for WebGPU etc"
46
- return np.array(self.get_matrix(), dtype=np.float32).reshape([4, 4])
38
+ return self.m.astype(np.float32)
47
39
 
48
40
  @classmethod
49
41
  def identity(cls):
@@ -55,36 +47,30 @@ class Mat4:
55
47
  def zero(cls):
56
48
  "class method to return a zero matrix"
57
49
  v = Mat4()
58
- v.m = [
59
- [0.0, 0.0, 0.0, 0.0],
60
- [0.0, 0.0, 0.0, 0.0],
61
- [0.0, 0.0, 0.0, 0.0],
62
- [0.0, 0.0, 0.0, 0.0],
63
- ]
50
+ v.m = np.zeros((4, 4), dtype=np.float64)
64
51
  return v
65
52
 
66
53
  @classmethod
67
54
  def from_list(cls, lst):
68
55
  "class method to create mat4 from list"
69
56
  v = Mat4()
70
- v.m = lst
71
- if not v._is_square():
72
- if len(lst) == 16: # can convert
73
- v.m = [lst[0:4], lst[4:8], lst[8:12], lst[12:16]]
57
+ if isinstance(lst, list) and len(lst) == 4 and all(isinstance(row, list) for row in lst):
58
+ # 2D list
59
+ if all(len(row) == 4 for row in lst):
60
+ v.m = np.array(lst, dtype=np.float64)
74
61
  return v
75
- else:
62
+ elif any(len(row) != 4 for row in lst):
76
63
  raise Mat4NotSquare
77
- else:
64
+ elif isinstance(lst, list) and len(lst) == 16:
65
+ # flat list - reshape to 4x4 in row-major order
66
+ v.m = np.array(lst, dtype=np.float64).reshape(4, 4, order="C")
78
67
  return v
79
-
80
- def _is_square(self) -> bool:
81
- "ensure matrix is square"
82
- return len(self.m) == 4 and all(len(i) == 4 for i in self.m)
68
+ else:
69
+ raise Mat4NotSquare
83
70
 
84
71
  def to_list(self):
85
72
  "convert matrix to list"
86
- # flatten to single array
87
- return functools.reduce(operator.concat, self.m)
73
+ return self.m.flatten("C").tolist()
88
74
 
89
75
  def copy(self) -> "Mat4":
90
76
  """Create a copy of the matrix.
@@ -93,48 +79,86 @@ class Mat4:
93
79
  A new Mat4 instance with the same values.
94
80
  """
95
81
  new_mat = Mat4()
96
- new_mat.m = copy.deepcopy(self.m)
82
+ new_mat.m = self.m.copy()
97
83
  return new_mat
98
84
 
99
85
  def transpose(self):
100
86
  "transpose this matrix"
101
- self.m = [list(item) for item in zip(*self.m, strict=False)]
87
+ self.m = self.m.T
102
88
 
103
89
  def get_transpose(self):
104
90
  "return a new matrix as the transpose of ourself"
105
91
  m = Mat4()
106
- m.m = [list(item) for item in zip(*self.m, strict=False)]
92
+ m.m = self.m.T.copy()
107
93
  return m
108
94
 
109
95
  @classmethod
110
96
  def scale(cls, x: float, y: float, z: float):
111
- "return a new matrix as scale"
112
- a = Mat4() # identity by default
113
- a.m[0][0] = x
114
- a.m[1][1] = y
115
- a.m[2][2] = z
97
+ """return a scale matrix resetting to identity first
98
+
99
+ Parameters
100
+ ----------
101
+ x : float
102
+ uniform scale in the x axis
103
+ y : float
104
+ uniform scale in the y axis
105
+ z : float
106
+ uniform scale in the z axis
107
+
108
+ .. highlight:: python
109
+ .. code-block:: python
110
+
111
+ scale=Mat4.scale(2.0,1.0,3.0)
112
+
113
+ Returns
114
+ -------
115
+ Mat3
116
+ matrix with diagonals set to the scale
117
+
118
+ """
119
+ a = Mat4()
120
+ a.m[0, 0] = x
121
+ a.m[1, 1] = y
122
+ a.m[2, 2] = z
116
123
  return a
117
124
 
118
125
  @classmethod
119
126
  def translate(cls, x: float, y: float, z: float):
120
127
  "return a new matrix as translation"
121
- a = Mat4() # identity by default
122
- a.m[3][0] = x
123
- a.m[3][1] = y
124
- a.m[3][2] = z
128
+ a = Mat4()
129
+ a.m[3, 0] = x
130
+ a.m[3, 1] = y
131
+ a.m[3, 2] = z
125
132
  return a
126
133
 
127
134
  @classmethod
128
135
  def rotate_x(cls, angle):
129
- "return a rotation around the X axis by angle degrees"
136
+ """return a rotation around the X axis by angle degrees
137
+
138
+ Parameters
139
+ ----------
140
+ angle : float
141
+ angle in degrees
142
+
143
+ .. highlight:: python
144
+ .. code-block:: python
145
+
146
+ rotate_x=Mat3.rotate_x(90.0)
147
+
148
+ Returns
149
+ -------
150
+ Mat3
151
+ matrix with rotation set to the angle
152
+
153
+ """
130
154
  a = Mat4()
131
155
  beta = math.radians(angle)
132
156
  sr = math.sin(beta)
133
157
  cr = math.cos(beta)
134
- a.m[1][1] = cr
135
- a.m[1][2] = sr
136
- a.m[2][1] = -sr
137
- a.m[2][2] = cr
158
+ a.m[1, 1] = cr
159
+ a.m[1, 2] = sr
160
+ a.m[2, 1] = -sr
161
+ a.m[2, 2] = cr
138
162
  return a
139
163
 
140
164
  @classmethod
@@ -144,10 +168,10 @@ class Mat4:
144
168
  beta = math.radians(angle)
145
169
  sr = math.sin(beta)
146
170
  cr = math.cos(beta)
147
- a.m[0][0] = cr
148
- a.m[0][2] = -sr
149
- a.m[2][0] = sr
150
- a.m[2][2] = cr
171
+ a.m[0, 0] = cr
172
+ a.m[0, 2] = -sr
173
+ a.m[2, 0] = sr
174
+ a.m[2, 2] = cr
151
175
  return a
152
176
 
153
177
  @classmethod
@@ -157,18 +181,18 @@ class Mat4:
157
181
  beta = math.radians(angle)
158
182
  sr = math.sin(beta)
159
183
  cr = math.cos(beta)
160
- a.m[0][0] = cr
161
- a.m[0][1] = sr
162
- a.m[1][0] = -sr
163
- a.m[1][1] = cr
184
+ a.m[0, 0] = cr
185
+ a.m[0, 1] = sr
186
+ a.m[1, 0] = -sr
187
+ a.m[1, 1] = cr
164
188
  return a
165
189
 
166
190
  def __getitem__(self, idx):
167
- "access array elements remember this is a list of lists [[4],[4],[4],[4]]"
168
- return self.m[idx]
191
+ "access array elements"
192
+ return self.m[idx].tolist()
169
193
 
170
194
  def __setitem__(self, idx, item):
171
- "set items remember this is a list of lists [[4],[4],[4],[4]]"
195
+ "set items"
172
196
  self.m[idx] = item
173
197
 
174
198
  def __mul__(self, rhs):
@@ -179,288 +203,99 @@ class Mat4:
179
203
  rhs : float int
180
204
  multiply each matrix element by rhs
181
205
 
182
- raises : Mat3Error
206
+ raises : Mat4Error
183
207
  if rhs is not a number
184
208
  """
185
209
  if isinstance(rhs, (int, float)):
186
- for i in range(0, len(self.m)):
187
- for j in range(0, len(self.m[i])):
188
- self.m[i][j] *= rhs
189
- return self
210
+ result = Mat4()
211
+ result.m = self.m * rhs
212
+ return result
190
213
  raise Mat4Error
191
214
 
192
215
  def _mat_mul(self, rhs):
193
- "matrix mult internal function"
194
- # fmt: off
195
- a00 = self.m[0][0] # cache values for speed? (works in C++ not sure about python)
196
- a01 = self.m[0][1]
197
- a02 = self.m[0][2]
198
- a03 = self.m[0][3]
199
- a10 = self.m[1][0]
200
- a11 = self.m[1][1]
201
- a12 = self.m[1][2]
202
- a13 = self.m[1][3]
203
- a20 = self.m[2][0]
204
- a21 = self.m[2][1]
205
- a22 = self.m[2][2]
206
- a23 = self.m[2][3]
207
- a30 = self.m[3][0]
208
- a31 = self.m[3][1]
209
- a32 = self.m[3][2]
210
- a33 = self.m[3][3]
211
- b00 = rhs.m[0][0]
212
- b01 = rhs.m[0][1]
213
- b02 = rhs.m[0][2]
214
- b03 = rhs.m[0][3]
215
- b10 = rhs.m[1][0]
216
- b11 = rhs.m[1][1]
217
- b12 = rhs.m[1][2]
218
- b13 = rhs.m[1][3]
219
- b20 = rhs.m[2][0]
220
- b21 = rhs.m[2][1]
221
- b22 = rhs.m[2][2]
222
- b23 = rhs.m[2][3]
223
- b30 = rhs.m[3][0]
224
- b31 = rhs.m[3][1]
225
- b32 = rhs.m[3][2]
226
- b33 = rhs.m[3][3]
227
- ret=Mat4() # result mat4
228
- ret.m[0][0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30
229
- ret.m[0][1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31
230
- ret.m[0][2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32
231
- ret.m[0][3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33
232
- ret.m[1][0] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30
233
- ret.m[1][1] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31
234
- ret.m[1][2] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32
235
- ret.m[1][3] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33
236
- ret.m[2][0] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30
237
- ret.m[2][1] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31
238
- ret.m[2][2] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32
239
- ret.m[2][3] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33
240
- ret.m[3][0] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30
241
- ret.m[3][1] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31
242
- ret.m[3][2] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32
243
- ret.m[3][3] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33
244
- return ret
245
- # fmt: on
216
+ "matrix mult for 3D OpenGL style graphics"
217
+ result = Mat4()
218
+ # Use numpy's @ operator which does standard matrix multiplication
219
+ result.m = rhs.m @ self.m
220
+ return result
246
221
 
247
222
  def __matmul__(self, rhs):
248
- from .vec4 import Vec4 # note relative import here
223
+ from .vec4 import Vec4
249
224
 
250
- "multiply matrix by another matrix"
225
+ "multiply matrix by another matrix or vector"
251
226
  if isinstance(rhs, Mat4):
252
227
  return self._mat_mul(rhs)
253
- elif isinstance(rhs, (Vec4, Vec4)):
254
- # fmt: off
255
- return Vec4(
256
- rhs.x * self.m[0][0] + rhs.y * self.m[0][1]+ rhs.z * self.m[0][2]+ rhs.w * self.m[0][3],
257
- rhs.x * self.m[1][0]+ rhs.y * self.m[1][1]+ rhs.z * self.m[1][2]+ rhs.w * self.m[1][3],
258
- rhs.x * self.m[2][0]+ rhs.y * self.m[2][1]+ rhs.z * self.m[2][2]+ rhs.w * self.m[2][3],
259
- rhs.x * self.m[3][0]+ rhs.y * self.m[3][1]+ rhs.z * self.m[3][2]+ rhs.w * self.m[3][3])
260
- # fmt: on
228
+ elif isinstance(rhs, Vec4):
229
+ # Vector transformation
230
+ # vec = np.array([rhs.x, rhs.y, rhs.z, rhs.w], dtype=np.float64)
231
+ res = self.m @ rhs._data
232
+ return Vec4(res[0], res[1], res[2], res[3])
261
233
  else:
262
234
  raise Mat4Error
263
235
 
264
236
  def __str__(self):
265
- return f"[{self.m[0]}\n{self.m[1]}\n{self.m[2]}\n{self.m[3]}]"
266
-
267
- def _addfunc(self, rhs):
268
- "internal add function"
269
- temp = Mat4()
270
- for i in range(0, len(temp.m)):
271
- temp.m[i] = [a + b for a, b in zip(self.m[i], rhs.m[i], strict=False)]
272
- return temp
237
+ rows = [self.m[i].tolist() for i in range(4)]
238
+ return f"[{rows[0]}\n{rows[1]}\n{rows[2]}\n{rows[3]}]"
273
239
 
274
240
  def __add__(self, rhs):
275
241
  "piecewise addition of elements"
276
- return self._addfunc(rhs)
242
+ result = Mat4()
243
+ result.m = self.m + rhs.m
244
+ return result
277
245
 
278
246
  def __iadd__(self, rhs):
279
247
  "piecewise addition of elements to this"
280
- return self._addfunc(rhs)
281
-
282
- def _sub(self, rhs):
283
- "internal sub function"
284
- temp = Mat4()
285
- for i in range(0, len(temp.m)):
286
- temp.m[i] = [a - b for a, b in zip(self.m[i], rhs.m[i], strict=False)]
287
- return temp
248
+ result = Mat4()
249
+ result.m = self.m + rhs.m
250
+ return result
288
251
 
289
252
  def __sub__(self, rhs):
290
253
  "piecewise subtraction of elements"
291
- return self._sub(rhs)
254
+ result = Mat4()
255
+ result.m = self.m - rhs.m
256
+ return result
292
257
 
293
258
  def __isub__(self, rhs):
294
259
  "piecewise subtraction of elements to this"
295
- return self._sub(rhs)
260
+ result = Mat4()
261
+ result.m = self.m - rhs.m
262
+ return result
296
263
 
297
264
  def determinant(self):
298
265
  "determinant of matrix"
299
- # Our matrices are 4.4 only, so we can just write the full formula instead of a complex algorithm.
300
- return (
301
- self.m[0][0] * self.m[1][1] * self.m[2][2] * self.m[3][3]
302
- - self.m[0][0] * self.m[1][1] * self.m[2][3] * self.m[3][2]
303
- + self.m[0][0] * self.m[1][2] * self.m[2][3] * self.m[3][1]
304
- - self.m[0][0] * self.m[1][2] * self.m[2][1] * self.m[3][3]
305
- + self.m[0][0] * self.m[1][3] * self.m[2][1] * self.m[3][2]
306
- - self.m[0][0] * self.m[1][3] * self.m[2][2] * self.m[3][1]
307
- - self.m[1][0] * self.m[2][1] * self.m[3][2] * self.m[0][3]
308
- + self.m[1][0] * self.m[2][1] * self.m[0][2] * self.m[3][3]
309
- - self.m[1][0] * self.m[3][1] * self.m[0][2] * self.m[2][3]
310
- + self.m[1][0] * self.m[3][1] * self.m[2][2] * self.m[0][3]
311
- - self.m[1][0] * self.m[0][1] * self.m[2][2] * self.m[3][3]
312
- + self.m[1][0] * self.m[0][1] * self.m[3][2] * self.m[2][3]
313
- + self.m[2][0] * self.m[3][1] * self.m[0][2] * self.m[1][3]
314
- - self.m[2][0] * self.m[3][1] * self.m[1][2] * self.m[0][3]
315
- + self.m[2][0] * self.m[0][1] * self.m[1][2] * self.m[3][3]
316
- - self.m[2][0] * self.m[0][1] * self.m[3][2] * self.m[1][3]
317
- + self.m[2][0] * self.m[1][1] * self.m[3][2] * self.m[0][3]
318
- - self.m[2][0] * self.m[1][1] * self.m[0][2] * self.m[3][3]
319
- - self.m[3][0] * self.m[0][1] * self.m[1][2] * self.m[2][3]
320
- + self.m[3][0] * self.m[0][1] * self.m[2][2] * self.m[1][3]
321
- - self.m[3][0] * self.m[1][1] * self.m[2][2] * self.m[0][3]
322
- + self.m[3][0] * self.m[1][1] * self.m[0][2] * self.m[2][3]
323
- - self.m[3][0] * self.m[2][1] * self.m[0][2] * self.m[1][3]
324
- + self.m[3][0] * self.m[2][1] * self.m[1][2] * self.m[0][3]
325
- )
266
+ return np.linalg.det(self.m)
326
267
 
327
268
  def inverse(self):
328
269
  "Inverse of matrix raise MatrixError if not calculable"
329
270
  try:
330
- det = self.determinant()
331
- tmp = Mat4()
332
- invdet = 1.0 / det
333
- tmp.m[0][0] = (
334
- self.m[1][1] * self.m[2][2] * self.m[3][3]
335
- + self.m[1][2] * self.m[2][3] * self.m[3][1]
336
- + self.m[1][3] * self.m[2][1] * self.m[3][2]
337
- - self.m[1][1] * self.m[3][2] * self.m[2][3]
338
- - self.m[1][2] * self.m[2][1] * self.m[3][3]
339
- - self.m[1][3] * self.m[2][2] * self.m[3][1]
340
- ) * invdet
341
- tmp.m[0][1] = (
342
- self.m[0][1] * self.m[2][3] * self.m[3][2]
343
- + self.m[0][2] * self.m[2][1] * self.m[3][3]
344
- + self.m[0][3] * self.m[2][2] * self.m[3][1]
345
- - self.m[0][1] * self.m[2][2] * self.m[3][3]
346
- - self.m[0][2] * self.m[2][3] * self.m[3][1]
347
- - self.m[0][3] * self.m[2][1] * self.m[3][2]
348
- ) * invdet
349
- tmp.m[0][2] = (
350
- self.m[0][1] * self.m[1][2] * self.m[3][3]
351
- + self.m[0][2] * self.m[1][3] * self.m[3][1]
352
- + self.m[0][3] * self.m[1][1] * self.m[3][2]
353
- - self.m[0][1] * self.m[1][3] * self.m[3][2]
354
- - self.m[0][2] * self.m[1][1] * self.m[3][3]
355
- - self.m[0][3] * self.m[1][2] * self.m[3][1]
356
- ) * invdet
357
- tmp.m[0][3] = (
358
- self.m[0][1] * self.m[1][3] * self.m[2][2]
359
- + self.m[0][2] * self.m[1][1] * self.m[2][3]
360
- + self.m[0][3] * self.m[1][2] * self.m[2][1]
361
- - self.m[0][1] * self.m[1][2] * self.m[2][3]
362
- - self.m[0][2] * self.m[1][3] * self.m[2][1]
363
- - self.m[0][3] * self.m[1][1] * self.m[2][2]
364
- ) * invdet
365
- tmp.m[1][0] = (
366
- self.m[1][0] * self.m[2][3] * self.m[3][2]
367
- + self.m[1][2] * self.m[2][0] * self.m[3][3]
368
- + self.m[1][3] * self.m[2][2] * self.m[3][0]
369
- - self.m[1][0] * self.m[2][2] * self.m[3][3]
370
- - self.m[1][2] * self.m[2][3] * self.m[3][0]
371
- - self.m[1][3] * self.m[2][0] * self.m[3][2]
372
- ) * invdet
373
- tmp.m[1][1] = (
374
- self.m[0][0] * self.m[2][2] * self.m[3][3]
375
- + self.m[0][2] * self.m[2][3] * self.m[3][0]
376
- + self.m[0][3] * self.m[2][0] * self.m[3][2]
377
- - self.m[0][0] * self.m[2][3] * self.m[3][2]
378
- - self.m[0][2] * self.m[2][0] * self.m[3][3]
379
- - self.m[0][3] * self.m[2][2] * self.m[3][0]
380
- ) * invdet
381
- tmp.m[1][2] = (
382
- self.m[0][0] * self.m[1][3] * self.m[3][2]
383
- + self.m[0][2] * self.m[1][0] * self.m[3][3]
384
- + self.m[0][3] * self.m[1][2] * self.m[3][0]
385
- - self.m[0][0] * self.m[1][2] * self.m[3][3]
386
- - self.m[0][2] * self.m[1][3] * self.m[3][0]
387
- - self.m[0][3] * self.m[1][0] * self.m[3][2]
388
- ) * invdet
389
- tmp.m[1][3] = (
390
- self.m[0][0] * self.m[1][2] * self.m[2][3]
391
- + self.m[0][2] * self.m[1][3] * self.m[2][0]
392
- + self.m[0][3] * self.m[1][0] * self.m[2][2]
393
- - self.m[0][0] * self.m[1][3] * self.m[2][2]
394
- - self.m[0][2] * self.m[1][0] * self.m[2][3]
395
- - self.m[0][3] * self.m[1][2] * self.m[2][0]
396
- ) * invdet
397
- tmp.m[2][0] = (
398
- self.m[1][0] * self.m[2][1] * self.m[3][3]
399
- + self.m[1][1] * self.m[2][3] * self.m[3][0]
400
- + self.m[1][3] * self.m[2][0] * self.m[3][1]
401
- - self.m[1][0] * self.m[2][3] * self.m[3][1]
402
- - self.m[1][1] * self.m[2][0] * self.m[3][3]
403
- - self.m[1][3] * self.m[2][1] * self.m[3][0]
404
- ) * invdet
405
- tmp.m[2][1] = (
406
- self.m[0][0] * self.m[2][3] * self.m[3][1]
407
- + self.m[0][1] * self.m[2][0] * self.m[3][3]
408
- + self.m[0][3] * self.m[2][1] * self.m[3][0]
409
- - self.m[0][0] * self.m[2][1] * self.m[3][3]
410
- - self.m[0][1] * self.m[2][3] * self.m[3][0]
411
- - self.m[0][3] * self.m[2][0] * self.m[3][1]
412
- ) * invdet
413
- tmp.m[2][2] = (
414
- self.m[0][0] * self.m[1][1] * self.m[3][3]
415
- + self.m[0][1] * self.m[1][3] * self.m[3][0]
416
- + self.m[0][3] * self.m[1][0] * self.m[3][1]
417
- - self.m[0][0] * self.m[1][3] * self.m[3][1]
418
- - self.m[0][1] * self.m[1][0] * self.m[3][3]
419
- - self.m[0][3] * self.m[1][1] * self.m[3][0]
420
- ) * invdet
421
- tmp.m[2][3] = (
422
- self.m[0][0] * self.m[1][3] * self.m[2][1]
423
- + self.m[0][1] * self.m[1][0] * self.m[2][3]
424
- + self.m[0][3] * self.m[1][1] * self.m[2][0]
425
- - self.m[0][0] * self.m[1][1] * self.m[2][3]
426
- - self.m[0][1] * self.m[1][3] * self.m[2][0]
427
- - self.m[0][3] * self.m[1][0] * self.m[2][1]
428
- ) * invdet
429
- tmp.m[3][0] = (
430
- self.m[1][0] * self.m[2][2] * self.m[3][1]
431
- + self.m[1][1] * self.m[2][0] * self.m[3][2]
432
- + self.m[1][2] * self.m[2][1] * self.m[3][0]
433
- - self.m[1][0] * self.m[2][1] * self.m[3][2]
434
- - self.m[1][1] * self.m[2][2] * self.m[3][0]
435
- - self.m[1][2] * self.m[2][0] * self.m[3][1]
436
- ) * invdet
437
- tmp.m[3][1] = (
438
- self.m[0][0] * self.m[2][1] * self.m[3][2]
439
- + self.m[0][1] * self.m[2][2] * self.m[3][0]
440
- + self.m[0][2] * self.m[2][0] * self.m[3][1]
441
- - self.m[0][0] * self.m[2][2] * self.m[3][1]
442
- - self.m[0][1] * self.m[2][0] * self.m[3][2]
443
- - self.m[0][2] * self.m[2][1] * self.m[3][0]
444
- ) * invdet
445
- tmp.m[3][2] = (
446
- self.m[0][0] * self.m[1][2] * self.m[3][1]
447
- + self.m[0][1] * self.m[1][0] * self.m[3][2]
448
- + self.m[0][2] * self.m[1][1] * self.m[3][0]
449
- - self.m[0][0] * self.m[1][1] * self.m[3][2]
450
- - self.m[0][1] * self.m[1][2] * self.m[3][0]
451
- - self.m[0][2] * self.m[1][0] * self.m[3][1]
452
- ) * invdet
453
- tmp.m[3][3] = (
454
- self.m[0][0] * self.m[1][1] * self.m[2][2]
455
- + self.m[0][1] * self.m[1][2] * self.m[2][0]
456
- + self.m[0][2] * self.m[1][0] * self.m[2][1]
457
- - self.m[0][0] * self.m[1][2] * self.m[2][1]
458
- - self.m[0][1] * self.m[1][0] * self.m[2][2]
459
- - self.m[0][2] * self.m[1][1] * self.m[2][0]
460
- ) * invdet
461
- return tmp
462
- except ZeroDivisionError:
271
+ result = Mat4()
272
+ result.m = np.linalg.inv(self.m)
273
+ return result
274
+ except np.linalg.LinAlgError:
463
275
  raise Mat4Error
464
276
 
465
277
  def __repr__(self) -> str:
466
- return f"Mat4({self.m})"
278
+ rows = [self.m[i].tolist() for i in range(4)]
279
+ return f"Mat4({rows})"
280
+
281
+ def __eq__(self, other):
282
+ """Value-based equality for Mat4: compare underlying matrices numerically.
283
+
284
+ Returns NotImplemented for non-Mat4 types so Python can try reflected comparisons
285
+ or handle it appropriately.
286
+ """
287
+ if not isinstance(other, Mat4):
288
+ return NotImplemented
289
+ # self.m and other.m should be numpy arrays; compare with tolerance
290
+ return np.allclose(self.m, other.m, rtol=1e-8, atol=1e-12)
291
+
292
+ def __ne__(self, other):
293
+ """Value-based not equality for Mat4: compare underlying matrices numerically.
294
+
295
+ Returns NotImplemented for non-Mat4 types so Python can try reflected comparisons
296
+ or handle it appropriately.
297
+ """
298
+ if not isinstance(other, Mat4):
299
+ return NotImplemented
300
+ # self.m and other.m should be numpy arrays; compare with tolerance
301
+ return not np.allclose(self.m, other.m, rtol=1e-8, atol=1e-12)