ncca-ngl 0.3.5__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 +0 -2
- ncca/ngl/vec2.py +59 -302
- ncca/ngl/vec2_array.py +79 -28
- ncca/ngl/vec3.py +60 -350
- ncca/ngl/vec3_array.py +76 -23
- ncca/ngl/vec4.py +90 -200
- 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.5.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.5.dist-info/RECORD +0 -82
- {ncca_ngl-0.3.5.dist-info → ncca_ngl-0.5.0.dist-info}/WHEEL +0 -0
ncca/ngl/PrimData/pack_arrays.py
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
|
|
3
|
-
import numpy as np
|
|
4
3
|
import pathlib
|
|
5
4
|
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
6
7
|
files = pathlib.Path(".").glob("*.npy")
|
|
7
8
|
|
|
8
9
|
data = {}
|
|
9
10
|
for f in files:
|
|
10
11
|
data[str(f.stem)] = np.load(f)
|
|
11
|
-
# arrays.append(np.load(f))
|
|
12
|
-
# names.append(str(f.stem))
|
|
13
12
|
print(data.keys())
|
|
14
13
|
|
|
15
14
|
|
ncca/ngl/__init__.py
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
from importlib.metadata import PackageNotFoundError, version
|
|
3
3
|
|
|
4
4
|
try:
|
|
5
|
-
__version__ = version("ncca-ngl")
|
|
6
|
-
except PackageNotFoundError:
|
|
5
|
+
__version__ = version("ncca-ngl") # pragma: no cover
|
|
6
|
+
except PackageNotFoundError: # pragma: no cover
|
|
7
7
|
__version__ = "0.0.0"
|
|
8
|
-
|
|
9
8
|
__author__ = "Jon Macey jmacey@bournemouth.ac.uk"
|
|
10
9
|
__license__ = "MIT"
|
|
11
10
|
|
|
@@ -16,7 +15,7 @@ from .bezier_curve import BezierCurve
|
|
|
16
15
|
from .first_person_camera import FirstPersonCamera
|
|
17
16
|
from .image import Image, ImageModes
|
|
18
17
|
from .log import logger
|
|
19
|
-
from .mat2 import Mat2
|
|
18
|
+
from .mat2 import Mat2, Mat2Error, Mat2NotSquare
|
|
20
19
|
from .mat3 import Mat3, Mat3Error, Mat3NotSquare
|
|
21
20
|
from .mat4 import Mat4, Mat4Error, Mat4NotSquare
|
|
22
21
|
from .multi_buffer_vao import MultiBufferVAO
|
ncca/ngl/base_mesh.py
CHANGED
|
@@ -54,6 +54,24 @@ class BaseMesh:
|
|
|
54
54
|
"""
|
|
55
55
|
return all(len(f.vertex) == 3 for f in self.faces)
|
|
56
56
|
|
|
57
|
+
def _should_skip_vao_creation(self, reset_vao: bool) -> bool:
|
|
58
|
+
"""Check if VAO creation should be skipped."""
|
|
59
|
+
if self.vao is None:
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
if reset_vao:
|
|
63
|
+
logger.warning("VAO exist so returning")
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
logger.warning("Creating new VAO")
|
|
67
|
+
return False
|
|
68
|
+
|
|
69
|
+
def _validate_triangular_mesh(self) -> None:
|
|
70
|
+
"""Validate that the mesh is composed of triangles."""
|
|
71
|
+
if not self.is_triangular():
|
|
72
|
+
logger.error("Can only create VBO from all Triangle data at present")
|
|
73
|
+
raise RuntimeError("Can only create VBO from all Triangle data at present")
|
|
74
|
+
|
|
57
75
|
def create_vao(self, reset_vao: bool = False) -> None:
|
|
58
76
|
"""
|
|
59
77
|
Create a Vertex Array Object (VAO) for the mesh.
|
|
@@ -64,20 +82,14 @@ class BaseMesh:
|
|
|
64
82
|
Raises:
|
|
65
83
|
RuntimeError: If the mesh is not composed entirely of triangles.
|
|
66
84
|
"""
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
data_pack_type = 0
|
|
76
|
-
if self.is_triangular():
|
|
77
|
-
data_pack_type = gl.GL_TRIANGLES
|
|
78
|
-
if data_pack_type == 0:
|
|
79
|
-
logger.error("Can only create VBO from all Triangle data at present")
|
|
80
|
-
raise RuntimeError("Can only create VBO from all Triangle data at present")
|
|
85
|
+
|
|
86
|
+
# Handle existing VAO based on reset_vao flag
|
|
87
|
+
if self._should_skip_vao_creation(reset_vao):
|
|
88
|
+
return
|
|
89
|
+
# Validate mesh is triangular
|
|
90
|
+
self._validate_triangular_mesh()
|
|
91
|
+
|
|
92
|
+
data_pack_type = gl.GL_TRIANGLES
|
|
81
93
|
|
|
82
94
|
@dataclass
|
|
83
95
|
class VertData:
|
|
@@ -123,9 +135,7 @@ class BaseMesh:
|
|
|
123
135
|
vbo_mesh.append(d)
|
|
124
136
|
|
|
125
137
|
mesh_data = np.concatenate([v.as_array() for v in vbo_mesh]).astype(np.float32)
|
|
126
|
-
self.vao = vao_factory.VAOFactory.create_vao(
|
|
127
|
-
vao_factory.VAOType.SIMPLE, data_pack_type
|
|
128
|
-
)
|
|
138
|
+
self.vao = vao_factory.VAOFactory.create_vao(vao_factory.VAOType.SIMPLE, data_pack_type)
|
|
129
139
|
with self.vao as vao:
|
|
130
140
|
mesh_size = len(mesh_data) // 8
|
|
131
141
|
vao.set_data(VertexData(mesh_data, mesh_size))
|
|
@@ -137,9 +147,7 @@ class BaseMesh:
|
|
|
137
147
|
vao.set_vertex_attribute_pointer(2, 2, gl.GL_FLOAT, 8 * 4, 6 * 4)
|
|
138
148
|
vao.set_num_indices(mesh_size)
|
|
139
149
|
self.calc_dimensions()
|
|
140
|
-
self.bbox = BBox.from_extents(
|
|
141
|
-
self.min_x, self.max_x, self.min_y, self.max_y, self.min_z, self.max_z
|
|
142
|
-
)
|
|
150
|
+
self.bbox = BBox.from_extents(self.min_x, self.max_x, self.min_y, self.max_y, self.min_z, self.max_z)
|
|
143
151
|
|
|
144
152
|
def calc_dimensions(self) -> None:
|
|
145
153
|
"""
|
ncca/ngl/image.py
CHANGED
|
@@ -34,9 +34,7 @@ class Image:
|
|
|
34
34
|
if mode == ImageModes.GRAY:
|
|
35
35
|
self._data = np.zeros((height, width), dtype=np.uint8)
|
|
36
36
|
else:
|
|
37
|
-
self._data = np.zeros(
|
|
38
|
-
(height, width, len(mode.value)), dtype=np.uint8
|
|
39
|
-
)
|
|
37
|
+
self._data = np.zeros((height, width, len(mode.value)), dtype=np.uint8)
|
|
40
38
|
else:
|
|
41
39
|
self._data = None
|
|
42
40
|
|
ncca/ngl/mat2.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
|
+
Mat2 class with NumPy implementation
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
2
6
|
|
|
3
7
|
from .vec2 import Vec2
|
|
4
8
|
|
|
@@ -7,36 +11,40 @@ class Mat2Error(Exception):
|
|
|
7
11
|
pass
|
|
8
12
|
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
class Mat2NotSquare(Exception):
|
|
15
|
+
"""If we try to construct from a non square (2x2) value or 4 elements this exception will be thrown"""
|
|
16
|
+
|
|
17
|
+
pass
|
|
11
18
|
|
|
12
19
|
|
|
13
20
|
class Mat2:
|
|
14
21
|
__slots__ = ["m"]
|
|
15
22
|
|
|
16
|
-
def __init__(self
|
|
23
|
+
def __init__(self):
|
|
17
24
|
"""
|
|
18
25
|
Initialize a 2x2 matrix.
|
|
19
26
|
|
|
20
|
-
Args:
|
|
21
|
-
m (list): A 2D list representing the matrix.
|
|
22
|
-
If not provided, an identity matrix is created.
|
|
23
|
-
"""
|
|
24
|
-
if m is None:
|
|
25
|
-
self.m = copy.deepcopy(_identity)
|
|
26
|
-
elif isinstance(m, list) and len(m) == 4 and not isinstance(m[0], list):
|
|
27
|
-
self.m = [m[0:2], m[2:4]]
|
|
28
|
-
else:
|
|
29
|
-
self.m = m
|
|
30
27
|
|
|
31
|
-
@classmethod
|
|
32
|
-
def from_list(cls, m: list[float]):
|
|
33
28
|
"""
|
|
34
|
-
|
|
29
|
+
self.m = np.eye(2, dtype=np.float64)
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
""
|
|
39
|
-
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_list(cls, lst):
|
|
33
|
+
"class method to create mat2 from list"
|
|
34
|
+
v = Mat2()
|
|
35
|
+
if isinstance(lst, list) and len(lst) == 2 and all(isinstance(row, list) for row in lst):
|
|
36
|
+
# 2D list
|
|
37
|
+
if all(len(row) == 2 for row in lst):
|
|
38
|
+
v.m = np.array(lst, dtype=np.float64)
|
|
39
|
+
return v
|
|
40
|
+
elif any(len(row) != 2 for row in lst):
|
|
41
|
+
raise Mat2NotSquare
|
|
42
|
+
elif isinstance(lst, list) and len(lst) == 4:
|
|
43
|
+
# flat list - reshape to 2x2 in row-major order
|
|
44
|
+
v.m = np.array(lst, dtype=np.float64).reshape(2, 2, order="C")
|
|
45
|
+
return v
|
|
46
|
+
else:
|
|
47
|
+
raise Mat2NotSquare
|
|
40
48
|
|
|
41
49
|
def get_matrix(self) -> list[float]:
|
|
42
50
|
"""
|
|
@@ -45,7 +53,7 @@ class Mat2:
|
|
|
45
53
|
Returns:
|
|
46
54
|
list[float]: A flat list of floats.
|
|
47
55
|
"""
|
|
48
|
-
return
|
|
56
|
+
return self.m.flatten("C").tolist()
|
|
49
57
|
|
|
50
58
|
def to_numpy(self):
|
|
51
59
|
"""
|
|
@@ -54,9 +62,7 @@ class Mat2:
|
|
|
54
62
|
Returns:
|
|
55
63
|
np.ndarray: The matrix as a NumPy array.
|
|
56
64
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return np.array(self.get_matrix()).reshape([2, 2])
|
|
65
|
+
return self.m.astype(np.float32)
|
|
60
66
|
|
|
61
67
|
@classmethod
|
|
62
68
|
def identity(cls) -> "Mat2":
|
|
@@ -66,9 +72,28 @@ class Mat2:
|
|
|
66
72
|
Returns:
|
|
67
73
|
Mat2: A new identity Mat2 object.
|
|
68
74
|
"""
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
return cls()
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def zero(cls):
|
|
79
|
+
"""class method to return a new zero matrix
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
Mat2
|
|
84
|
+
new Mat2 matrix as all zeros
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
v = Mat2()
|
|
88
|
+
v.m = np.zeros((2, 2), dtype=np.float64)
|
|
89
|
+
return v
|
|
90
|
+
|
|
91
|
+
def _mat_mul(self, rhs):
|
|
92
|
+
"matrix mult for 3D OpenGL style graphics"
|
|
93
|
+
result = Mat2()
|
|
94
|
+
# Use numpy's @ operator which does standard matrix multiplication
|
|
95
|
+
result.m = rhs.m @ self.m
|
|
96
|
+
return result
|
|
72
97
|
|
|
73
98
|
def __matmul__(self, rhs):
|
|
74
99
|
"""
|
|
@@ -77,8 +102,7 @@ class Mat2:
|
|
|
77
102
|
Args:
|
|
78
103
|
rhs (Mat2 | Vec2): The right-hand side operand.
|
|
79
104
|
If Mat2, perform matrix multiplication.
|
|
80
|
-
If Vec2, transform the
|
|
81
|
-
r by the matrix.
|
|
105
|
+
If Vec2, transform the vector by the matrix.
|
|
82
106
|
|
|
83
107
|
Returns:
|
|
84
108
|
Mat2: Resulting matrix from matrix multiplication.
|
|
@@ -90,29 +114,12 @@ class Mat2:
|
|
|
90
114
|
if isinstance(rhs, Mat2):
|
|
91
115
|
return self._mat_mul(rhs)
|
|
92
116
|
elif isinstance(rhs, Vec2):
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
)
|
|
117
|
+
vec = np.array([rhs.x, rhs.y], dtype=np.float64)
|
|
118
|
+
res = self.m @ vec
|
|
119
|
+
return Vec2(res[0], res[1])
|
|
97
120
|
else:
|
|
98
121
|
raise ValueError(f"Can only multiply by Mat2 or Vec2, not {type(rhs)}")
|
|
99
122
|
|
|
100
|
-
def _mat_mul(self, other):
|
|
101
|
-
"""
|
|
102
|
-
Internal method to perform matrix multiplication.
|
|
103
|
-
|
|
104
|
-
Args:
|
|
105
|
-
other (Mat2): The right-hand side matrix.
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
Mat2: Result of matrix multiplication.
|
|
109
|
-
"""
|
|
110
|
-
ret = Mat2()
|
|
111
|
-
for i in range(2):
|
|
112
|
-
for j in range(2):
|
|
113
|
-
ret.m[i][j] = sum(self.m[i][k] * other.m[k][j] for k in range(2))
|
|
114
|
-
return ret
|
|
115
|
-
|
|
116
123
|
def __str__(self) -> str:
|
|
117
124
|
"""
|
|
118
125
|
String representation of the matrix.
|
|
@@ -120,12 +127,11 @@ class Mat2:
|
|
|
120
127
|
Returns:
|
|
121
128
|
str: The string representation.
|
|
122
129
|
"""
|
|
123
|
-
return f"Mat2({self.m[0]}, {self.m[1]})"
|
|
130
|
+
return f"Mat2({self.m[0].tolist()}, {self.m[1].tolist()})"
|
|
124
131
|
|
|
125
132
|
def to_list(self):
|
|
126
133
|
"convert matrix to list in column-major order"
|
|
127
|
-
|
|
128
|
-
return [item for sublist in zip(*self.m, strict=False) for item in sublist]
|
|
134
|
+
return self.m.flatten("C").tolist()
|
|
129
135
|
|
|
130
136
|
def copy(self) -> "Mat2":
|
|
131
137
|
"""Create a copy of the matrix.
|
|
@@ -134,5 +140,25 @@ class Mat2:
|
|
|
134
140
|
A new Mat2 instance with the same values.
|
|
135
141
|
"""
|
|
136
142
|
new_mat = Mat2()
|
|
137
|
-
new_mat.m =
|
|
143
|
+
new_mat.m = self.m.copy()
|
|
138
144
|
return new_mat
|
|
145
|
+
|
|
146
|
+
def __eq__(self, rhs):
|
|
147
|
+
"""Value-based equality for Mat2: compare underlying matrices numerically.
|
|
148
|
+
Returns NotImplemented for non-Mat2 types so Python can try reflected comparisons
|
|
149
|
+
or handle it appropriately.
|
|
150
|
+
"""
|
|
151
|
+
if not isinstance(rhs, Mat2):
|
|
152
|
+
return NotImplemented
|
|
153
|
+
# self.m and other.m should be numpy arrays; compare with tolerance
|
|
154
|
+
return bool(np.allclose(self.m, rhs.m, rtol=1e-8, atol=1e-12))
|
|
155
|
+
|
|
156
|
+
def __ne__(self, rhs):
|
|
157
|
+
"""Value-based equality for Mat2: compare underlying matrices numerically.
|
|
158
|
+
Returns NotImplemented for non-Mat2 types so Python can try reflected comparisons
|
|
159
|
+
or handle it appropriately.
|
|
160
|
+
"""
|
|
161
|
+
if not isinstance(rhs, Mat2):
|
|
162
|
+
return NotImplemented
|
|
163
|
+
# self.m and other.m should be numpy arrays; compare with tolerance
|
|
164
|
+
return not bool(np.allclose(self.m, rhs.m, rtol=1e-8, atol=1e-12))
|