Skip to content

Commit 79738cf

Browse files
committed
surface vectors, lint
1 parent fe8d2fe commit 79738cf

4 files changed

Lines changed: 271 additions & 10 deletions

File tree

ngsolve_webgpu/geometry.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,7 @@ def update(self, options: RenderOptions):
227227

228228
def get_bounding_box(self):
229229
pmin, pmax = self.geo.shape.bounding_box
230-
return ([pmin[0], pmin[1], pmin[2]],
231-
[pmax[0], pmax[1], pmax[2]])
230+
return ([pmin[0], pmin[1], pmin[2]], [pmax[0], pmax[1], pmax[2]])
232231

233232
def render(self, encoder):
234233
for r in self.render_objects:

ngsolve_webgpu/mesh.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,7 @@ def _create_data(self):
255255

256256
def get_bounding_box(self):
257257
pmin, pmax = self.mesh.bounding_box
258-
return ([pmin[0], pmin[1], pmin[2]],
259-
[pmax[0], pmax[1], pmax[2]])
258+
return ([pmin[0], pmin[1], pmin[2]], [pmax[0], pmax[1], pmax[2]])
260259

261260
def get_buffers(self):
262261
for eltype in self.elements:
@@ -329,19 +328,20 @@ def get_bindings(self):
329328
def get_shader_code(self):
330329
return read_shader_file("ngsolve/mesh.wgsl")
331330

331+
332332
class MeshElements2d(BaseMeshElements2d):
333333
fragment_entry_point = "fragment2dElement"
334334

335-
def __init__(self, data: MeshData, clipping=None,
336-
colors: list | None = None,
337-
label="MeshElements2d"):
335+
def __init__(
336+
self, data: MeshData, clipping=None, colors: list | None = None, label="MeshElements2d"
337+
):
338338
super().__init__(data, label=label, clipping=clipping)
339339
if colors is None:
340340
mesh = data.mesh
341341
colors = [[int(ci * 255) for ci in fd.color] for fd in mesh.FaceDescriptors()]
342-
self.colormap = Colormap(colormap=colors, minval=-0.5, maxval=len(colors)-0.5)
342+
self.colormap = Colormap(colormap=colors, minval=-0.5, maxval=len(colors) - 0.5)
343343
self.colormap.discrete = 0
344-
self.colormap.n_colors = 4*len(colors)
344+
self.colormap.n_colors = 4 * len(colors)
345345

346346
def update(self, options: RenderOptions):
347347
super().update(options)
@@ -353,7 +353,7 @@ def get_bindings(self):
353353

354354
class MeshWireframe2d(BaseMeshElements2d):
355355
depthBias: int = 0
356-
depthBiasSlopeScale: float = 0.
356+
depthBiasSlopeScale: float = 0.0
357357
topology: PrimitiveTopology = PrimitiveTopology.line_strip
358358
color = (0, 0, 0, 1)
359359
fragment_entry_point: str = "fragmentWireframe2d"
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#import ngsolve/eval/trig
2+
#import ngsolve/uniforms
3+
#import colormap
4+
5+
@group(0) @binding(21) var<storage, read_write> count_vectors: atomic<u32>;
6+
@group(0) @binding(22) var<storage, read_write> positions: array<f32>;
7+
@group(0) @binding(23) var<storage, read_write> directions: array<f32>;
8+
@group(0) @binding(25) var<storage, read_write> values: array<f32>;
9+
@group(0) @binding(24) var<uniform> u_ntrigs: u32;
10+
11+
@compute @workgroup_size(256)
12+
fn compute_surface_vectors(@builtin(global_invocation_id) id: vec3<u32>) {
13+
for (var trigId = id.x; trigId<u_ntrigs; trigId+=256*1024) {
14+
var vid = 3 * vec3u(
15+
trigs[4 * trigId + 2],
16+
trigs[4 * trigId + 0],
17+
trigs[4 * trigId + 1]
18+
);
19+
20+
let p = array<vec3<f32>, 3>(
21+
vec3<f32>(vertices[vid[0] ], vertices[vid[0] + 1], vertices[vid[0] + 2]),
22+
vec3<f32>(vertices[vid[1] ], vertices[vid[1] + 1], vertices[vid[1] + 2]),
23+
vec3<f32>(vertices[vid[2] ], vertices[vid[2] + 1], vertices[vid[2] + 2])
24+
);
25+
26+
// let pmin = min(p[0], min(p[1], p[2]));
27+
let pmin = vec3f(-1, -1, -1);
28+
let rad = 1.0;
29+
let swap_lam = false;
30+
31+
var dir: u32 =0;
32+
var dir1: u32 =0;
33+
var dir2: u32 =0;
34+
35+
let n = cross (p[1]-p[0], p[2]-p[0]);
36+
let na = abs(n);
37+
if (na[0] > na[1] && na[0] > na[2]) {
38+
dir = 0;
39+
}
40+
else if (na[1] > na[2]) {
41+
dir = 1;
42+
}
43+
else {
44+
dir = 2;
45+
}
46+
47+
dir1 = (dir+1) % 3;
48+
dir2 = (dir1+1) % 3;
49+
50+
var p2d: array<vec2f, 3>;
51+
52+
for (var k: u32 = 0; k < 3; k++)
53+
{
54+
p2d[k] = vec2f((p[k][dir1] - pmin[dir1]) / (2*rad),
55+
(p[k][dir2] - pmin[dir2]) / (2*rad));
56+
}
57+
58+
var min2d = min(min(p2d[0], p2d[1]), p2d[2]);
59+
var max2d = max(max(p2d[0], p2d[1]), p2d[2]);
60+
61+
let m = mat2x2f(
62+
p2d[1] - p2d[0],
63+
p2d[2] - p2d[0]
64+
);
65+
let mdet = determinant(m);
66+
67+
let minv = 1.0/mdet * mat2x2f( m[1][1], -m[0][1], -m[1][0], m[0][0] );
68+
69+
let gridsize = 0.03;
70+
71+
let xmin = floor(min2d.x / gridsize) * gridsize;
72+
73+
for (var s = 0.0; s <= 1.; s += 1.0 * gridsize) {
74+
if (s >= min2d.x && s <= max2d.x)
75+
{
76+
for (var t = 0.; t <= 1.; t += 1.0 * gridsize) {
77+
if (t >= min2d.y && t <= max2d.y)
78+
{
79+
let lam = minv * (vec2f(s, t) - p2d[0]);
80+
81+
if (lam.x >= 0 && lam.y >= 0 && lam.x+lam.y <= 1)
82+
{
83+
var cp = p[0] + lam.x * (p[1] - p[0]) + lam.y * (p[2] - p[0]);
84+
85+
if(@MODE@ == 0) {
86+
// just count
87+
atomicAdd(&count_vectors, 1);
88+
}
89+
else {
90+
// write output to buffer
91+
let v = evalTrigVec3(&u_function_values_2d, trigId, lam);
92+
93+
let val = length(v);
94+
var scale = (val - u_cmap_uniforms.min) / (u_cmap_uniforms.max - u_cmap_uniforms.min);
95+
scale = 2 * gridsize * clamp(scale, 0.5, 1.0);
96+
let direction = scale * normalize(v) ;
97+
let index = atomicAdd(&count_vectors, 1);
98+
if (u_curvature_values_2d[0] != -1.) {
99+
cp = evalTrigVec3(&u_curvature_values_2d, trigId, lam);
100+
}
101+
cp += 0.5 * gridsize * normalize(n);
102+
103+
positions[index*3+0] = cp[0];
104+
positions[index*3+1] = cp[1];
105+
positions[index*3+2] = cp[2];
106+
values[index] = val;
107+
directions[index*3+0] = direction[0];
108+
directions[index*3+1] = direction[1];
109+
directions[index*3+2] = direction[2];
110+
}
111+
112+
}
113+
114+
115+
}
116+
}
117+
}
118+
}
119+
}
120+
}

ngsolve_webgpu/surface_vectors.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import ngsolve as ngs
2+
import numpy as np
3+
from webgpu.shapes import ShapeRenderer, generate_cone, generate_cylinder
4+
from webgpu.utils import (
5+
BufferUsage,
6+
BufferBinding,
7+
ReadBuffer,
8+
UniformBinding,
9+
read_buffer,
10+
read_shader_file,
11+
run_compute_shader,
12+
uniform_from_array,
13+
buffer_from_array,
14+
write_array_to_buffer,
15+
)
16+
17+
from .cf import FunctionData, MeshData, Binding as FunctionBinding
18+
from .mesh import Binding as MeshBinding
19+
from .mesh import ElType
20+
21+
22+
class SurfaceVectors(ShapeRenderer):
23+
def __init__(
24+
self,
25+
function_data: FunctionData,
26+
mesh: MeshData,
27+
grid_size: float = 0.02,
28+
):
29+
self.function_data = function_data
30+
self.mesh = mesh
31+
32+
bbox = mesh.get_bounding_box()
33+
grid_size = np.linalg.norm(np.array(bbox[1]) - np.array(bbox[0])) * grid_size
34+
35+
cyl = generate_cylinder(8, 0.05, 0.5, bottom_face=True)
36+
cone = generate_cone(8, 0.2, 0.5, bottom_face=True)
37+
arrow = cyl + cone.move((0, 0, 0.5))
38+
39+
super().__init__(arrow, None, None)
40+
# self.scale_mode = ShapeRenderer.SCALE_Z
41+
42+
def get_bounding_box(self):
43+
return self.mesh.get_bounding_box()
44+
45+
def get_compute_bindings(self):
46+
return []
47+
48+
def compute_vectors(self):
49+
self.u_nvectors = buffer_from_array(
50+
np.array([0], dtype=np.uint32),
51+
label="n_vectors",
52+
usage=BufferUsage.STORAGE | BufferUsage.COPY_DST | BufferUsage.COPY_SRC,
53+
)
54+
55+
mesh_buffers = self.mesh.get_buffers()
56+
func_buffers = self.function_data.get_buffers()
57+
n_trigs = self.mesh.num_elements[ElType.TRIG]
58+
self.u_ntrigs = uniform_from_array(np.array([n_trigs], dtype=np.uint32), label="n_trigs")
59+
60+
positions = buffer_from_array(np.array([0], dtype=np.float32), label="positions")
61+
directions = buffer_from_array(np.array([0], dtype=np.float32), label="positions")
62+
values = buffer_from_array(np.array([0], dtype=np.float32), label="positions")
63+
64+
bindings = [
65+
*self.colormap.get_bindings(),
66+
BufferBinding(MeshBinding.VERTICES, mesh_buffers["vertices"]),
67+
BufferBinding(MeshBinding.TRIGS_INDEX, mesh_buffers[ElType.TRIG]),
68+
BufferBinding(22, positions, read_only=False),
69+
BufferBinding(23, directions, read_only=False),
70+
BufferBinding(25, values, read_only=False),
71+
BufferBinding(21, self.u_nvectors, read_only=False),
72+
UniformBinding(24, self.u_ntrigs),
73+
BufferBinding(MeshBinding.CURVATURE_VALUES_2D, mesh_buffers["curvature_2d"]),
74+
# BufferBinding(MeshBinding.DEFORMATION_VALUES, mesh_buffers["deformation_2d"]),
75+
UniformBinding(MeshBinding.DEFORMATION_SCALE, mesh_buffers["deformation_scale"]),
76+
BufferBinding(FunctionBinding.FUNCTION_VALUES_2D, func_buffers["data_2d"]),
77+
]
78+
run_compute_shader(
79+
read_shader_file("ngsolve/surface_vectors.wgsl"),
80+
bindings,
81+
min(n_trigs // 256 + 1, 1024),
82+
entry_point="compute_surface_vectors",
83+
defines={
84+
"MODE": 0,
85+
"MAX_EVAL_ORDER": self.function_data.order,
86+
"MAX_EVAL_ORDER_VEC3": self.function_data.order,
87+
},
88+
)
89+
90+
self.n_vectors = int(read_buffer(self.u_nvectors, np.uint32)[0])
91+
write_array_to_buffer(self.u_nvectors, np.array([0], dtype=np.uint32))
92+
buffers = {}
93+
for name in ["positions", "directions"]:
94+
buffers[name] = self.device.createBuffer(
95+
size=3 * 4 * self.n_vectors,
96+
usage=BufferUsage.STORAGE | BufferUsage.COPY_SRC,
97+
label=name,
98+
)
99+
buffers["values"] = self.device.createBuffer(
100+
size=4 * self.n_vectors,
101+
usage=BufferUsage.STORAGE | BufferUsage.COPY_SRC,
102+
label="values",
103+
)
104+
105+
bindings = [
106+
*self.colormap.get_bindings(),
107+
BufferBinding(MeshBinding.VERTICES, mesh_buffers["vertices"]),
108+
BufferBinding(MeshBinding.TRIGS_INDEX, mesh_buffers[ElType.TRIG]),
109+
BufferBinding(22, buffers["positions"], read_only=False),
110+
BufferBinding(23, buffers["directions"], read_only=False),
111+
BufferBinding(25, buffers["values"], read_only=False),
112+
BufferBinding(21, self.u_nvectors, read_only=False),
113+
BufferBinding(MeshBinding.CURVATURE_VALUES_2D, mesh_buffers["curvature_2d"]),
114+
BufferBinding(FunctionBinding.FUNCTION_VALUES_2D, func_buffers["data_2d"]),
115+
# BufferBinding(MeshBinding.DEFORMATION_VALUES, mesh_buffers["deformation_2d"]),
116+
UniformBinding(MeshBinding.DEFORMATION_SCALE, mesh_buffers["deformation_scale"]),
117+
UniformBinding(24, self.u_ntrigs),
118+
]
119+
120+
run_compute_shader(
121+
read_shader_file("ngsolve/surface_vectors.wgsl"),
122+
bindings,
123+
min(n_trigs // 256 + 1, 1024),
124+
entry_point="compute_surface_vectors",
125+
defines={
126+
"MODE": 1,
127+
"MAX_EVAL_ORDER": self.function_data.order,
128+
"MAX_EVAL_ORDER_VEC3": self.function_data.order,
129+
},
130+
)
131+
132+
self.positions = read_buffer(buffers["positions"], np.float32).reshape(-1)
133+
self.values = read_buffer(buffers["values"], np.float32).reshape(-1)
134+
self.directions = read_buffer(buffers["directions"], np.float32).reshape(-1)
135+
136+
def update(self, options):
137+
self.mesh.update(options)
138+
self.function_data.update(options)
139+
self.colormap.update(options)
140+
self.compute_vectors()
141+
super().update(options)
142+
return

0 commit comments

Comments
 (0)