lsurf 1.0.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.
- lsurf/__init__.py +471 -0
- lsurf/analysis/__init__.py +107 -0
- lsurf/analysis/healpix_utils.py +418 -0
- lsurf/analysis/sphere_viz.py +1280 -0
- lsurf/cli/__init__.py +48 -0
- lsurf/cli/build.py +398 -0
- lsurf/cli/config_schema.py +318 -0
- lsurf/cli/gui_cmd.py +76 -0
- lsurf/cli/interactive.py +850 -0
- lsurf/cli/main.py +81 -0
- lsurf/cli/run.py +806 -0
- lsurf/detectors/__init__.py +266 -0
- lsurf/detectors/analysis.py +289 -0
- lsurf/detectors/base.py +284 -0
- lsurf/detectors/constant_size_rings.py +485 -0
- lsurf/detectors/directional.py +45 -0
- lsurf/detectors/extended/__init__.py +73 -0
- lsurf/detectors/extended/local_sphere.py +353 -0
- lsurf/detectors/extended/recording_sphere.py +368 -0
- lsurf/detectors/planar.py +45 -0
- lsurf/detectors/protocol.py +187 -0
- lsurf/detectors/recording_spheres.py +63 -0
- lsurf/detectors/results.py +1140 -0
- lsurf/detectors/small/__init__.py +79 -0
- lsurf/detectors/small/directional.py +330 -0
- lsurf/detectors/small/planar.py +401 -0
- lsurf/detectors/small/spherical.py +450 -0
- lsurf/detectors/spherical.py +45 -0
- lsurf/geometry/__init__.py +199 -0
- lsurf/geometry/builder.py +478 -0
- lsurf/geometry/cell.py +228 -0
- lsurf/geometry/cell_geometry.py +247 -0
- lsurf/geometry/detector_arrays.py +1785 -0
- lsurf/geometry/geometry.py +222 -0
- lsurf/geometry/surface_analysis.py +375 -0
- lsurf/geometry/validation.py +91 -0
- lsurf/gui/__init__.py +51 -0
- lsurf/gui/app.py +903 -0
- lsurf/gui/core/__init__.py +39 -0
- lsurf/gui/core/scene.py +343 -0
- lsurf/gui/core/simulation.py +264 -0
- lsurf/gui/renderers/__init__.py +40 -0
- lsurf/gui/renderers/ray_renderer.py +353 -0
- lsurf/gui/renderers/source_renderer.py +505 -0
- lsurf/gui/renderers/surface_renderer.py +477 -0
- lsurf/gui/views/__init__.py +48 -0
- lsurf/gui/views/config_editor.py +3199 -0
- lsurf/gui/views/properties.py +257 -0
- lsurf/gui/views/results.py +291 -0
- lsurf/gui/views/scene_tree.py +180 -0
- lsurf/gui/views/viewport_3d.py +555 -0
- lsurf/gui/views/visualizations.py +712 -0
- lsurf/materials/__init__.py +169 -0
- lsurf/materials/base/__init__.py +64 -0
- lsurf/materials/base/full_inhomogeneous.py +208 -0
- lsurf/materials/base/grid_inhomogeneous.py +319 -0
- lsurf/materials/base/homogeneous.py +342 -0
- lsurf/materials/base/material_field.py +527 -0
- lsurf/materials/base/simple_inhomogeneous.py +418 -0
- lsurf/materials/base/spectral_inhomogeneous.py +497 -0
- lsurf/materials/implementations/__init__.py +120 -0
- lsurf/materials/implementations/data/alpha_values_typical_atmosphere_updated.txt +24 -0
- lsurf/materials/implementations/duct_atmosphere.py +390 -0
- lsurf/materials/implementations/exponential_atmosphere.py +435 -0
- lsurf/materials/implementations/gaussian_lens.py +120 -0
- lsurf/materials/implementations/interpolated_data.py +123 -0
- lsurf/materials/implementations/layered_atmosphere.py +134 -0
- lsurf/materials/implementations/linear_gradient.py +109 -0
- lsurf/materials/implementations/linsley_atmosphere.py +764 -0
- lsurf/materials/implementations/standard_materials.py +126 -0
- lsurf/materials/implementations/turbulent_atmosphere.py +135 -0
- lsurf/materials/implementations/us_standard_atmosphere.py +149 -0
- lsurf/materials/utils/__init__.py +77 -0
- lsurf/materials/utils/constants.py +45 -0
- lsurf/materials/utils/device_functions.py +117 -0
- lsurf/materials/utils/dispersion.py +160 -0
- lsurf/materials/utils/factories.py +142 -0
- lsurf/propagation/__init__.py +91 -0
- lsurf/propagation/detector_gpu.py +67 -0
- lsurf/propagation/gpu_device_rays.py +294 -0
- lsurf/propagation/kernels/__init__.py +175 -0
- lsurf/propagation/kernels/absorption/__init__.py +61 -0
- lsurf/propagation/kernels/absorption/grid.py +240 -0
- lsurf/propagation/kernels/absorption/simple.py +232 -0
- lsurf/propagation/kernels/absorption/spectral.py +410 -0
- lsurf/propagation/kernels/detection/__init__.py +64 -0
- lsurf/propagation/kernels/detection/protocol.py +102 -0
- lsurf/propagation/kernels/detection/spherical.py +255 -0
- lsurf/propagation/kernels/device_functions.py +790 -0
- lsurf/propagation/kernels/fresnel/__init__.py +64 -0
- lsurf/propagation/kernels/fresnel/protocol.py +97 -0
- lsurf/propagation/kernels/fresnel/standard.py +258 -0
- lsurf/propagation/kernels/intersection/__init__.py +79 -0
- lsurf/propagation/kernels/intersection/annular_plane.py +207 -0
- lsurf/propagation/kernels/intersection/bounded_plane.py +205 -0
- lsurf/propagation/kernels/intersection/plane.py +166 -0
- lsurf/propagation/kernels/intersection/protocol.py +95 -0
- lsurf/propagation/kernels/intersection/signed_distance.py +742 -0
- lsurf/propagation/kernels/intersection/sphere.py +190 -0
- lsurf/propagation/kernels/propagation/__init__.py +85 -0
- lsurf/propagation/kernels/propagation/grid.py +527 -0
- lsurf/propagation/kernels/propagation/protocol.py +105 -0
- lsurf/propagation/kernels/propagation/simple.py +460 -0
- lsurf/propagation/kernels/propagation/spectral.py +875 -0
- lsurf/propagation/kernels/registry.py +331 -0
- lsurf/propagation/kernels/surface/__init__.py +72 -0
- lsurf/propagation/kernels/surface/bisection.py +232 -0
- lsurf/propagation/kernels/surface/detection.py +402 -0
- lsurf/propagation/kernels/surface/reduction.py +166 -0
- lsurf/propagation/propagator_protocol.py +222 -0
- lsurf/propagation/propagators/__init__.py +101 -0
- lsurf/propagation/propagators/detector_handler.py +354 -0
- lsurf/propagation/propagators/factory.py +200 -0
- lsurf/propagation/propagators/fresnel_handler.py +305 -0
- lsurf/propagation/propagators/gpu_gradient.py +566 -0
- lsurf/propagation/propagators/gpu_surface_propagator.py +707 -0
- lsurf/propagation/propagators/gradient.py +429 -0
- lsurf/propagation/propagators/intersection_handler.py +327 -0
- lsurf/propagation/propagators/material_propagator.py +398 -0
- lsurf/propagation/propagators/signed_distance_handler.py +522 -0
- lsurf/propagation/propagators/spectral_gpu_gradient.py +553 -0
- lsurf/propagation/propagators/surface_interaction.py +616 -0
- lsurf/propagation/propagators/surface_propagator.py +719 -0
- lsurf/py.typed +1 -0
- lsurf/simulation/__init__.py +70 -0
- lsurf/simulation/config.py +164 -0
- lsurf/simulation/orchestrator.py +462 -0
- lsurf/simulation/result.py +299 -0
- lsurf/simulation/simulation.py +262 -0
- lsurf/sources/__init__.py +128 -0
- lsurf/sources/base.py +264 -0
- lsurf/sources/collimated.py +252 -0
- lsurf/sources/custom.py +409 -0
- lsurf/sources/diverging.py +228 -0
- lsurf/sources/gaussian.py +272 -0
- lsurf/sources/parallel_from_positions.py +197 -0
- lsurf/sources/point.py +172 -0
- lsurf/sources/uniform_diverging.py +258 -0
- lsurf/surfaces/__init__.py +184 -0
- lsurf/surfaces/cpu/__init__.py +50 -0
- lsurf/surfaces/cpu/curved_wave.py +463 -0
- lsurf/surfaces/cpu/gerstner_wave.py +381 -0
- lsurf/surfaces/cpu/wave_params.py +118 -0
- lsurf/surfaces/gpu/__init__.py +72 -0
- lsurf/surfaces/gpu/annular_plane.py +453 -0
- lsurf/surfaces/gpu/bounded_plane.py +390 -0
- lsurf/surfaces/gpu/curved_wave.py +483 -0
- lsurf/surfaces/gpu/gerstner_wave.py +377 -0
- lsurf/surfaces/gpu/multi_curved_wave.py +520 -0
- lsurf/surfaces/gpu/plane.py +299 -0
- lsurf/surfaces/gpu/recording_sphere.py +587 -0
- lsurf/surfaces/gpu/sphere.py +311 -0
- lsurf/surfaces/protocol.py +336 -0
- lsurf/surfaces/registry.py +373 -0
- lsurf/utilities/__init__.py +175 -0
- lsurf/utilities/detector_analysis.py +814 -0
- lsurf/utilities/fresnel.py +628 -0
- lsurf/utilities/interactions.py +1215 -0
- lsurf/utilities/propagation.py +602 -0
- lsurf/utilities/ray_data.py +532 -0
- lsurf/utilities/recording_sphere.py +745 -0
- lsurf/utilities/time_spread.py +463 -0
- lsurf/visualization/__init__.py +329 -0
- lsurf/visualization/absorption_plots.py +334 -0
- lsurf/visualization/atmospheric_plots.py +754 -0
- lsurf/visualization/common.py +348 -0
- lsurf/visualization/detector_plots.py +1350 -0
- lsurf/visualization/detector_sphere_plots.py +1173 -0
- lsurf/visualization/fresnel_plots.py +1061 -0
- lsurf/visualization/ocean_simulation_plots.py +999 -0
- lsurf/visualization/polarization_plots.py +916 -0
- lsurf/visualization/raytracing_plots.py +1521 -0
- lsurf/visualization/ring_detector_plots.py +1867 -0
- lsurf/visualization/time_spread_plots.py +531 -0
- lsurf-1.0.0.dist-info/METADATA +381 -0
- lsurf-1.0.0.dist-info/RECORD +180 -0
- lsurf-1.0.0.dist-info/WHEEL +5 -0
- lsurf-1.0.0.dist-info/entry_points.txt +2 -0
- lsurf-1.0.0.dist-info/licenses/LICENSE +32 -0
- lsurf-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
# The Clear BSD License
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2026 Tobias Heibges
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted (subject to the limitations in the disclaimer
|
|
8
|
+
# below) provided that the following conditions are met:
|
|
9
|
+
#
|
|
10
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer.
|
|
12
|
+
#
|
|
13
|
+
# * Redistributions in binary form must reproduce the above copyright
|
|
14
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
15
|
+
# documentation and/or other materials provided with the distribution.
|
|
16
|
+
#
|
|
17
|
+
# * Neither the name of the copyright holder nor the names of its
|
|
18
|
+
# contributors may be used to endorse or promote products derived from this
|
|
19
|
+
# software without specific prior written permission.
|
|
20
|
+
#
|
|
21
|
+
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
|
22
|
+
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
23
|
+
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
24
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
25
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
26
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
27
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
28
|
+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
29
|
+
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
30
|
+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
GPU Signed Distance Functions for All Surface Types
|
|
36
|
+
|
|
37
|
+
Generic dispatch kernel that computes signed distance for any surface type
|
|
38
|
+
based on geometry_id. This allows GPU-accelerated intersection detection
|
|
39
|
+
for all surfaces via bisection on signed distance values.
|
|
40
|
+
|
|
41
|
+
Device functions compute the signed distance formula for each geometry type.
|
|
42
|
+
The main kernel dispatches to the appropriate device function based on ID.
|
|
43
|
+
|
|
44
|
+
Geometry IDs:
|
|
45
|
+
1 = Plane: signed distance to infinite plane
|
|
46
|
+
2 = Sphere: signed distance to sphere surface
|
|
47
|
+
3 = Gerstner wave: flat-earth wave surface
|
|
48
|
+
4 = Curved wave: spherical-earth wave surface (single wave)
|
|
49
|
+
5 = Multi-wave curved: spherical-earth with up to 8 wave components
|
|
50
|
+
7 = Annular plane: ring-shaped planar surface (detector)
|
|
51
|
+
|
|
52
|
+
This module contains ONLY pure CUDA kernels - no GPU memory management.
|
|
53
|
+
The propagator layer is responsible for cuda.to_device() and copy_to_host().
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
import math
|
|
57
|
+
|
|
58
|
+
from ..registry import IntersectionKernelID, register_kernel
|
|
59
|
+
|
|
60
|
+
# GPU support is optional
|
|
61
|
+
try:
|
|
62
|
+
from numba import cuda
|
|
63
|
+
|
|
64
|
+
HAS_CUDA = True
|
|
65
|
+
except ImportError:
|
|
66
|
+
|
|
67
|
+
class _FakeCuda:
|
|
68
|
+
"""Fake cuda module for when numba is not installed."""
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def jit(*args, **kwargs):
|
|
72
|
+
"""Return a no-op decorator."""
|
|
73
|
+
|
|
74
|
+
def decorator(func):
|
|
75
|
+
return func
|
|
76
|
+
|
|
77
|
+
if args and callable(args[0]):
|
|
78
|
+
return args[0]
|
|
79
|
+
return decorator
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def is_available():
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def grid(n):
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def synchronize():
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
cuda = _FakeCuda() # type: ignore[assignment]
|
|
94
|
+
HAS_CUDA = False
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# =============================================================================
|
|
98
|
+
# Constants
|
|
99
|
+
# =============================================================================
|
|
100
|
+
|
|
101
|
+
GRAVITY = 9.81 # Gravity for deep water dispersion
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# =============================================================================
|
|
105
|
+
# Device Functions - Signed Distance
|
|
106
|
+
# =============================================================================
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@cuda.jit(device=True)
|
|
110
|
+
def _device_plane_sd(x: float, y: float, z: float, params) -> float:
|
|
111
|
+
"""
|
|
112
|
+
Signed distance from point to plane.
|
|
113
|
+
|
|
114
|
+
Plane: signed_distance = dot(p - point, normal)
|
|
115
|
+
Positive on normal side, negative on back side.
|
|
116
|
+
|
|
117
|
+
Parameters (from PlaneSurface.get_gpu_parameters):
|
|
118
|
+
p0: normal_x
|
|
119
|
+
p1: normal_y
|
|
120
|
+
p2: normal_z
|
|
121
|
+
p3: point_x
|
|
122
|
+
p4: point_y
|
|
123
|
+
p5: point_z
|
|
124
|
+
"""
|
|
125
|
+
nx, ny, nz = params[0], params[1], params[2]
|
|
126
|
+
px, py, pz = params[3], params[4], params[5]
|
|
127
|
+
|
|
128
|
+
return nx * (x - px) + ny * (y - py) + nz * (z - pz)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@cuda.jit(device=True)
|
|
132
|
+
def _device_sphere_sd(x: float, y: float, z: float, params) -> float:
|
|
133
|
+
"""
|
|
134
|
+
Signed distance from point to sphere surface.
|
|
135
|
+
|
|
136
|
+
Sphere: signed_distance = |p - center| - radius
|
|
137
|
+
Positive outside, negative inside.
|
|
138
|
+
|
|
139
|
+
Parameters (from SphereSurface.get_gpu_parameters):
|
|
140
|
+
p0: center_x
|
|
141
|
+
p1: center_y
|
|
142
|
+
p2: center_z
|
|
143
|
+
p3: radius
|
|
144
|
+
"""
|
|
145
|
+
cx, cy, cz = params[0], params[1], params[2]
|
|
146
|
+
r = params[3]
|
|
147
|
+
|
|
148
|
+
dx = x - cx
|
|
149
|
+
dy = y - cy
|
|
150
|
+
dz = z - cz
|
|
151
|
+
|
|
152
|
+
dist = math.sqrt(dx * dx + dy * dy + dz * dz)
|
|
153
|
+
return dist - abs(r)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@cuda.jit(device=True)
|
|
157
|
+
def _device_gerstner_sd(x: float, y: float, z: float, params) -> float:
|
|
158
|
+
"""
|
|
159
|
+
Signed distance from point to Gerstner wave surface.
|
|
160
|
+
|
|
161
|
+
Wave: z = ref_z + A * cos(k * (dx*x + dy*y) - omega*t + phase)
|
|
162
|
+
Signed distance: z - wave_height (positive above, negative below)
|
|
163
|
+
|
|
164
|
+
Parameters (from GPUGerstnerWaveSurface.get_gpu_parameters):
|
|
165
|
+
p0: amplitude
|
|
166
|
+
p1: wave_number (k)
|
|
167
|
+
p2: dir_x (normalized)
|
|
168
|
+
p3: dir_y (normalized)
|
|
169
|
+
p4: reference_z
|
|
170
|
+
p5: phase
|
|
171
|
+
p6: time
|
|
172
|
+
"""
|
|
173
|
+
A = params[0]
|
|
174
|
+
k = params[1]
|
|
175
|
+
dir_x, dir_y = params[2], params[3]
|
|
176
|
+
ref_z = params[4]
|
|
177
|
+
phase = params[5]
|
|
178
|
+
time = params[6]
|
|
179
|
+
|
|
180
|
+
# Angular frequency from deep water dispersion: omega = sqrt(g*k)
|
|
181
|
+
omega = math.sqrt(GRAVITY * k)
|
|
182
|
+
|
|
183
|
+
# Phase at this position
|
|
184
|
+
phase_val = k * (dir_x * x + dir_y * y) - omega * time + phase
|
|
185
|
+
|
|
186
|
+
# Wave height
|
|
187
|
+
wave_height = ref_z + A * math.cos(phase_val)
|
|
188
|
+
|
|
189
|
+
return z - wave_height
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@cuda.jit(device=True)
|
|
193
|
+
def _device_curved_wave_sd(x: float, y: float, z: float, params) -> float:
|
|
194
|
+
"""
|
|
195
|
+
Signed distance from point to curved-earth wave surface.
|
|
196
|
+
|
|
197
|
+
The surface is Earth's sphere + wave perturbation.
|
|
198
|
+
Signed distance: |p - center| - (earth_radius + wave_height)
|
|
199
|
+
Positive outside, negative inside.
|
|
200
|
+
|
|
201
|
+
Uses numerically stable formulation to avoid catastrophic cancellation
|
|
202
|
+
when computing dist - R with large Earth radius values in float32.
|
|
203
|
+
|
|
204
|
+
For earth_center = (cx, cy, cz):
|
|
205
|
+
dist² - R² = (x-cx)² + (y-cy)² + (z-cz)² - R²
|
|
206
|
+
= x² + y² + z² - 2(x·cx + y·cy + z·cz) + (cx² + cy² + cz²) - R²
|
|
207
|
+
|
|
208
|
+
For the typical case center = (0, 0, -R):
|
|
209
|
+
dist² - R² = x² + y² + z² + 2zR (since cz = -R, so -2z·cz = 2zR, and cx²+cy²+cz²-R² = 0)
|
|
210
|
+
|
|
211
|
+
Parameters (from GPUCurvedWaveSurface.get_gpu_parameters):
|
|
212
|
+
p0: earth_center_x
|
|
213
|
+
p1: earth_center_y
|
|
214
|
+
p2: earth_center_z
|
|
215
|
+
p3: earth_radius
|
|
216
|
+
p4: amplitude
|
|
217
|
+
p5: wave_number (k)
|
|
218
|
+
p6: dir_x (normalized)
|
|
219
|
+
p7: dir_y (normalized)
|
|
220
|
+
p8: time
|
|
221
|
+
"""
|
|
222
|
+
cx, cy, cz = params[0], params[1], params[2]
|
|
223
|
+
earth_radius = params[3]
|
|
224
|
+
A = params[4]
|
|
225
|
+
k = params[5]
|
|
226
|
+
dir_x, dir_y = params[6], params[7]
|
|
227
|
+
time = params[8]
|
|
228
|
+
|
|
229
|
+
# Vector from Earth center to point
|
|
230
|
+
dx = x - cx
|
|
231
|
+
dy = y - cy
|
|
232
|
+
dz = z - cz
|
|
233
|
+
|
|
234
|
+
# Compute dist² and dist
|
|
235
|
+
dist_sq = dx * dx + dy * dy + dz * dz
|
|
236
|
+
dist = math.sqrt(dist_sq)
|
|
237
|
+
|
|
238
|
+
# Numerically stable computation of dist² - R²
|
|
239
|
+
# Expand: dist² - R² = x² + y² + z² - 2(x·cx + y·cy + z·cz) + (cx² + cy² + cz²) - R²
|
|
240
|
+
# For typical case (cx=0, cy=0, cz=-R), this simplifies to x² + y² + z² + 2zR
|
|
241
|
+
# which avoids subtracting two large ~O(R²) numbers
|
|
242
|
+
pos_sq = x * x + y * y + z * z
|
|
243
|
+
dot_pc = x * cx + y * cy + z * cz
|
|
244
|
+
center_sq = cx * cx + cy * cy + cz * cz
|
|
245
|
+
R_sq = earth_radius * earth_radius
|
|
246
|
+
|
|
247
|
+
# dist² - R² computed stably (small terms when near surface)
|
|
248
|
+
dist_sq_minus_R_sq = pos_sq - 2.0 * dot_pc + center_sq - R_sq
|
|
249
|
+
|
|
250
|
+
# Altitude above Earth: (dist² - R²) / (dist + R) = dist - R
|
|
251
|
+
altitude = dist_sq_minus_R_sq / (dist + earth_radius)
|
|
252
|
+
|
|
253
|
+
# Angular frequency from deep water dispersion
|
|
254
|
+
omega = math.sqrt(GRAVITY * k)
|
|
255
|
+
|
|
256
|
+
# Wave phase using x,y as local tangent coordinates
|
|
257
|
+
dot_val = dir_x * x + dir_y * y
|
|
258
|
+
phase_val = k * dot_val - omega * time
|
|
259
|
+
|
|
260
|
+
# Wave height
|
|
261
|
+
wave_height = A * math.cos(phase_val)
|
|
262
|
+
|
|
263
|
+
# Signed distance = altitude above Earth - wave_height
|
|
264
|
+
# Positive when above wave surface, negative when below
|
|
265
|
+
return altitude - wave_height
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@cuda.jit(device=True)
|
|
269
|
+
def _device_multi_curved_wave_sd(x: float, y: float, z: float, params) -> float:
|
|
270
|
+
"""
|
|
271
|
+
Signed distance from point to multi-wave curved-earth surface.
|
|
272
|
+
|
|
273
|
+
Supports up to 8 superimposed wave components on Earth's sphere.
|
|
274
|
+
Signed distance: |p - center| - (earth_radius + sum_of_wave_heights)
|
|
275
|
+
Positive outside, negative inside.
|
|
276
|
+
|
|
277
|
+
Uses numerically stable formulation to avoid catastrophic cancellation
|
|
278
|
+
when computing dist - R with large Earth radius values in float32.
|
|
279
|
+
|
|
280
|
+
Parameters (from GPUMultiCurvedWaveSurface.get_gpu_parameters):
|
|
281
|
+
p0: earth_center_x
|
|
282
|
+
p1: earth_center_y
|
|
283
|
+
p2: earth_center_z
|
|
284
|
+
p3: earth_radius
|
|
285
|
+
p4: time
|
|
286
|
+
p5: num_waves
|
|
287
|
+
p6-p7: reserved
|
|
288
|
+
|
|
289
|
+
For each wave i (i=0..7), starting at offset 8 + i*8:
|
|
290
|
+
- p[offset+0]: amplitude
|
|
291
|
+
- p[offset+1]: wave_number (k)
|
|
292
|
+
- p[offset+2]: dir_x (normalized)
|
|
293
|
+
- p[offset+3]: dir_y (normalized)
|
|
294
|
+
- p[offset+4]: phase
|
|
295
|
+
- p[offset+5]: steepness (unused in SD calculation)
|
|
296
|
+
- p[offset+6-7]: reserved
|
|
297
|
+
"""
|
|
298
|
+
cx, cy, cz = params[0], params[1], params[2]
|
|
299
|
+
earth_radius = params[3]
|
|
300
|
+
time = params[4]
|
|
301
|
+
num_waves = int(params[5])
|
|
302
|
+
|
|
303
|
+
# Vector from Earth center to point
|
|
304
|
+
dx = x - cx
|
|
305
|
+
dy = y - cy
|
|
306
|
+
dz = z - cz
|
|
307
|
+
|
|
308
|
+
# Compute dist² and dist
|
|
309
|
+
dist_sq = dx * dx + dy * dy + dz * dz
|
|
310
|
+
dist = math.sqrt(dist_sq)
|
|
311
|
+
|
|
312
|
+
# Numerically stable computation of dist² - R²
|
|
313
|
+
# Expand: dist² - R² = x² + y² + z² - 2(x·cx + y·cy + z·cz) + (cx² + cy² + cz²) - R²
|
|
314
|
+
pos_sq = x * x + y * y + z * z
|
|
315
|
+
dot_pc = x * cx + y * cy + z * cz
|
|
316
|
+
center_sq = cx * cx + cy * cy + cz * cz
|
|
317
|
+
R_sq = earth_radius * earth_radius
|
|
318
|
+
|
|
319
|
+
# dist² - R² computed stably (small terms when near surface)
|
|
320
|
+
dist_sq_minus_R_sq = pos_sq - 2.0 * dot_pc + center_sq - R_sq
|
|
321
|
+
|
|
322
|
+
# Altitude above Earth: (dist² - R²) / (dist + R) = dist - R
|
|
323
|
+
altitude = dist_sq_minus_R_sq / (dist + earth_radius)
|
|
324
|
+
|
|
325
|
+
# Sum wave heights from all components
|
|
326
|
+
total_wave_height = 0.0
|
|
327
|
+
for i in range(num_waves):
|
|
328
|
+
offset = 8 + i * 8
|
|
329
|
+
A = params[offset + 0]
|
|
330
|
+
k = params[offset + 1]
|
|
331
|
+
dir_x = params[offset + 2]
|
|
332
|
+
dir_y = params[offset + 3]
|
|
333
|
+
phase = params[offset + 4]
|
|
334
|
+
|
|
335
|
+
# Angular frequency from deep water dispersion
|
|
336
|
+
omega = math.sqrt(GRAVITY * k)
|
|
337
|
+
|
|
338
|
+
# Wave phase using x,y as local tangent coordinates
|
|
339
|
+
dot_val = dir_x * x + dir_y * y
|
|
340
|
+
phase_val = k * dot_val - omega * time + phase
|
|
341
|
+
|
|
342
|
+
# Accumulate wave height
|
|
343
|
+
total_wave_height += A * math.cos(phase_val)
|
|
344
|
+
|
|
345
|
+
# Signed distance = altitude above Earth - wave_height
|
|
346
|
+
# Positive when above wave surface, negative when below
|
|
347
|
+
return altitude - total_wave_height
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
# =============================================================================
|
|
351
|
+
# Device Functions - Surface Normal
|
|
352
|
+
# =============================================================================
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
@cuda.jit(device=True)
|
|
356
|
+
def _device_plane_normal(
|
|
357
|
+
x: float, y: float, z: float, params, out_nx, out_ny, out_nz
|
|
358
|
+
) -> tuple:
|
|
359
|
+
"""
|
|
360
|
+
Compute normal for plane surface.
|
|
361
|
+
|
|
362
|
+
For a plane, normal is constant everywhere.
|
|
363
|
+
Returns (nx, ny, nz) tuple.
|
|
364
|
+
"""
|
|
365
|
+
# Normal is stored directly in params
|
|
366
|
+
return params[0], params[1], params[2]
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
@cuda.jit(device=True)
|
|
370
|
+
def _device_sphere_normal(
|
|
371
|
+
x: float, y: float, z: float, params, out_nx, out_ny, out_nz
|
|
372
|
+
) -> tuple:
|
|
373
|
+
"""
|
|
374
|
+
Compute normal for sphere surface.
|
|
375
|
+
|
|
376
|
+
Normal points radially outward from center.
|
|
377
|
+
Returns (nx, ny, nz) tuple.
|
|
378
|
+
"""
|
|
379
|
+
cx, cy, cz = params[0], params[1], params[2]
|
|
380
|
+
r = params[3]
|
|
381
|
+
|
|
382
|
+
dx = x - cx
|
|
383
|
+
dy = y - cy
|
|
384
|
+
dz = z - cz
|
|
385
|
+
|
|
386
|
+
norm = math.sqrt(dx * dx + dy * dy + dz * dz)
|
|
387
|
+
if norm < 1e-12:
|
|
388
|
+
return 0.0, 0.0, 1.0
|
|
389
|
+
|
|
390
|
+
nx = dx / norm
|
|
391
|
+
ny = dy / norm
|
|
392
|
+
nz = dz / norm
|
|
393
|
+
|
|
394
|
+
# For negative radius (concave), flip normals
|
|
395
|
+
if r < 0:
|
|
396
|
+
nx, ny, nz = -nx, -ny, -nz
|
|
397
|
+
|
|
398
|
+
return nx, ny, nz
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
@cuda.jit(device=True)
|
|
402
|
+
def _device_gerstner_normal(
|
|
403
|
+
x: float, y: float, z: float, params, out_nx, out_ny, out_nz
|
|
404
|
+
) -> tuple:
|
|
405
|
+
"""
|
|
406
|
+
Compute normal for Gerstner wave surface.
|
|
407
|
+
|
|
408
|
+
Normal is computed from gradient of height field.
|
|
409
|
+
For z = ref_z + A*cos(theta):
|
|
410
|
+
dz/dx = -A * k * dir_x * sin(theta)
|
|
411
|
+
dz/dy = -A * k * dir_y * sin(theta)
|
|
412
|
+
Normal = (-dz/dx, -dz/dy, 1) normalized
|
|
413
|
+
|
|
414
|
+
Returns (nx, ny, nz) tuple.
|
|
415
|
+
"""
|
|
416
|
+
A = params[0]
|
|
417
|
+
k = params[1]
|
|
418
|
+
dir_x, dir_y = params[2], params[3]
|
|
419
|
+
phase = params[5]
|
|
420
|
+
time = params[6]
|
|
421
|
+
|
|
422
|
+
omega = math.sqrt(GRAVITY * k)
|
|
423
|
+
phase_val = k * (dir_x * x + dir_y * y) - omega * time + phase
|
|
424
|
+
|
|
425
|
+
sin_theta = math.sin(phase_val)
|
|
426
|
+
dz_dx = -A * k * dir_x * sin_theta
|
|
427
|
+
dz_dy = -A * k * dir_y * sin_theta
|
|
428
|
+
|
|
429
|
+
# Normal = (-dz/dx, -dz/dy, 1) normalized
|
|
430
|
+
nx = -dz_dx
|
|
431
|
+
ny = -dz_dy
|
|
432
|
+
nz = 1.0
|
|
433
|
+
|
|
434
|
+
norm = math.sqrt(nx * nx + ny * ny + nz * nz)
|
|
435
|
+
return nx / norm, ny / norm, nz / norm
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
@cuda.jit(device=True)
|
|
439
|
+
def _device_curved_wave_normal(
|
|
440
|
+
x: float, y: float, z: float, params, out_nx, out_ny, out_nz
|
|
441
|
+
) -> tuple:
|
|
442
|
+
"""
|
|
443
|
+
Compute normal for curved-earth wave surface.
|
|
444
|
+
|
|
445
|
+
This is a simplified version - for full accuracy, transform to local
|
|
446
|
+
tangent space. Here we compute radial + wave perturbation.
|
|
447
|
+
|
|
448
|
+
Returns (nx, ny, nz) tuple.
|
|
449
|
+
"""
|
|
450
|
+
cx, cy, cz = params[0], params[1], params[2]
|
|
451
|
+
A = params[4]
|
|
452
|
+
k = params[5]
|
|
453
|
+
dir_x, dir_y = params[6], params[7]
|
|
454
|
+
time = params[8]
|
|
455
|
+
|
|
456
|
+
# Radial direction from Earth center
|
|
457
|
+
dx = x - cx
|
|
458
|
+
dy = y - cy
|
|
459
|
+
dz = z - cz
|
|
460
|
+
dist = math.sqrt(dx * dx + dy * dy + dz * dz)
|
|
461
|
+
if dist < 1e-10:
|
|
462
|
+
return 0.0, 0.0, 1.0
|
|
463
|
+
|
|
464
|
+
radial_x = dx / dist
|
|
465
|
+
radial_y = dy / dist
|
|
466
|
+
radial_z = dz / dist
|
|
467
|
+
|
|
468
|
+
# Wave normal perturbation (simplified)
|
|
469
|
+
omega = math.sqrt(GRAVITY * k)
|
|
470
|
+
dot_val = dir_x * x + dir_y * y
|
|
471
|
+
phase_val = k * dot_val - omega * time
|
|
472
|
+
|
|
473
|
+
sin_theta = math.sin(phase_val)
|
|
474
|
+
WA = k * A
|
|
475
|
+
|
|
476
|
+
# Local wave gradient contribution (small perturbation)
|
|
477
|
+
# This is simplified - full version would transform to tangent space
|
|
478
|
+
nx_local = dir_x * WA * sin_theta
|
|
479
|
+
ny_local = dir_y * WA * sin_theta
|
|
480
|
+
nz_local = 1.0 - 0.5 * WA * sin_theta
|
|
481
|
+
|
|
482
|
+
# Combine radial with local perturbation (simplified blend)
|
|
483
|
+
nx = radial_x + 0.01 * nx_local
|
|
484
|
+
ny = radial_y + 0.01 * ny_local
|
|
485
|
+
nz = radial_z + 0.01 * nz_local
|
|
486
|
+
|
|
487
|
+
norm = math.sqrt(nx * nx + ny * ny + nz * nz)
|
|
488
|
+
if norm < 1e-12:
|
|
489
|
+
return radial_x, radial_y, radial_z
|
|
490
|
+
|
|
491
|
+
return nx / norm, ny / norm, nz / norm
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@cuda.jit(device=True)
|
|
495
|
+
def _device_multi_curved_wave_normal(
|
|
496
|
+
x: float, y: float, z: float, params, out_nx, out_ny, out_nz
|
|
497
|
+
) -> tuple:
|
|
498
|
+
"""
|
|
499
|
+
Compute normal for multi-wave curved-earth surface.
|
|
500
|
+
|
|
501
|
+
Combines radial direction with wave gradient perturbations from
|
|
502
|
+
all wave components.
|
|
503
|
+
|
|
504
|
+
Returns (nx, ny, nz) tuple.
|
|
505
|
+
"""
|
|
506
|
+
cx, cy, cz = params[0], params[1], params[2]
|
|
507
|
+
time = params[4]
|
|
508
|
+
num_waves = int(params[5])
|
|
509
|
+
|
|
510
|
+
# Radial direction from Earth center
|
|
511
|
+
dx = x - cx
|
|
512
|
+
dy = y - cy
|
|
513
|
+
dz = z - cz
|
|
514
|
+
dist = math.sqrt(dx * dx + dy * dy + dz * dz)
|
|
515
|
+
if dist < 1e-10:
|
|
516
|
+
return 0.0, 0.0, 1.0
|
|
517
|
+
|
|
518
|
+
radial_x = dx / dist
|
|
519
|
+
radial_y = dy / dist
|
|
520
|
+
radial_z = dz / dist
|
|
521
|
+
|
|
522
|
+
# Accumulate wave normal perturbations
|
|
523
|
+
nx_local = 0.0
|
|
524
|
+
ny_local = 0.0
|
|
525
|
+
nz_local = 1.0
|
|
526
|
+
|
|
527
|
+
for i in range(num_waves):
|
|
528
|
+
offset = 8 + i * 8
|
|
529
|
+
A = params[offset + 0]
|
|
530
|
+
k = params[offset + 1]
|
|
531
|
+
dir_x = params[offset + 2]
|
|
532
|
+
dir_y = params[offset + 3]
|
|
533
|
+
phase = params[offset + 4]
|
|
534
|
+
|
|
535
|
+
omega = math.sqrt(GRAVITY * k)
|
|
536
|
+
dot_val = dir_x * x + dir_y * y
|
|
537
|
+
phase_val = k * dot_val - omega * time + phase
|
|
538
|
+
|
|
539
|
+
sin_theta = math.sin(phase_val)
|
|
540
|
+
WA = k * A
|
|
541
|
+
|
|
542
|
+
# Accumulate gradient contributions
|
|
543
|
+
nx_local += dir_x * WA * sin_theta
|
|
544
|
+
ny_local += dir_y * WA * sin_theta
|
|
545
|
+
nz_local -= 0.5 * WA * sin_theta
|
|
546
|
+
|
|
547
|
+
# Normalize local normal
|
|
548
|
+
local_norm = math.sqrt(
|
|
549
|
+
nx_local * nx_local + ny_local * ny_local + nz_local * nz_local
|
|
550
|
+
)
|
|
551
|
+
if local_norm > 1e-10:
|
|
552
|
+
nx_local /= local_norm
|
|
553
|
+
ny_local /= local_norm
|
|
554
|
+
nz_local /= local_norm
|
|
555
|
+
|
|
556
|
+
# Combine radial with local perturbation (simplified blend)
|
|
557
|
+
nx = radial_x + 0.01 * nx_local
|
|
558
|
+
ny = radial_y + 0.01 * ny_local
|
|
559
|
+
nz = radial_z + 0.01 * nz_local
|
|
560
|
+
|
|
561
|
+
norm = math.sqrt(nx * nx + ny * ny + nz * nz)
|
|
562
|
+
if norm < 1e-12:
|
|
563
|
+
return radial_x, radial_y, radial_z
|
|
564
|
+
|
|
565
|
+
return nx / norm, ny / norm, nz / norm
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
@cuda.jit(device=True)
|
|
569
|
+
def _device_annular_plane_sd(x: float, y: float, z: float, params) -> float:
|
|
570
|
+
"""
|
|
571
|
+
Signed distance from point to annular plane.
|
|
572
|
+
|
|
573
|
+
For signed distance, we compute distance to the infinite plane.
|
|
574
|
+
Bounds checking (inner/outer radius) is done separately in intersection.
|
|
575
|
+
|
|
576
|
+
Plane: signed_distance = dot(p - center, normal)
|
|
577
|
+
Positive on normal side, negative on back side.
|
|
578
|
+
|
|
579
|
+
Parameters (from AnnularPlaneSurface.get_gpu_parameters):
|
|
580
|
+
p0: normal_x
|
|
581
|
+
p1: normal_y
|
|
582
|
+
p2: normal_z
|
|
583
|
+
p3: center_x
|
|
584
|
+
p4: center_y
|
|
585
|
+
p5: center_z
|
|
586
|
+
p6-11: u_axis, v_axis (not needed for signed distance)
|
|
587
|
+
p12: inner_radius_sq (not used in SD)
|
|
588
|
+
p13: outer_radius_sq (not used in SD)
|
|
589
|
+
"""
|
|
590
|
+
nx, ny, nz = params[0], params[1], params[2]
|
|
591
|
+
cx, cy, cz = params[3], params[4], params[5]
|
|
592
|
+
|
|
593
|
+
return nx * (x - cx) + ny * (y - cy) + nz * (z - cz)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
@cuda.jit(device=True)
|
|
597
|
+
def _device_annular_plane_normal(
|
|
598
|
+
x: float, y: float, z: float, params, out_nx, out_ny, out_nz
|
|
599
|
+
) -> tuple:
|
|
600
|
+
"""
|
|
601
|
+
Compute normal for annular plane surface.
|
|
602
|
+
|
|
603
|
+
For a plane, normal is constant everywhere.
|
|
604
|
+
Returns (nx, ny, nz) tuple.
|
|
605
|
+
"""
|
|
606
|
+
# Normal is stored directly in params
|
|
607
|
+
return params[0], params[1], params[2]
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
# =============================================================================
|
|
611
|
+
# Main Dispatch Kernels
|
|
612
|
+
# =============================================================================
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
@register_kernel(IntersectionKernelID.SIGNED_DISTANCE_GENERIC)
|
|
616
|
+
@cuda.jit
|
|
617
|
+
def kernel_signed_distance(
|
|
618
|
+
positions, # (N, 3) input positions
|
|
619
|
+
signed_dist_out, # (N,) output signed distances
|
|
620
|
+
geometry_id, # int: 1=plane, 2=sphere, 3=gerstner, 4=curved_wave, 5=multi_curved_wave
|
|
621
|
+
params, # (64,) surface parameters from get_gpu_parameters()
|
|
622
|
+
):
|
|
623
|
+
"""
|
|
624
|
+
CUDA kernel to compute signed distance from points to a surface.
|
|
625
|
+
|
|
626
|
+
Dispatches to appropriate device function based on geometry_id.
|
|
627
|
+
This kernel enables GPU-accelerated intersection detection for all
|
|
628
|
+
surface types via bisection on signed distance values.
|
|
629
|
+
|
|
630
|
+
Parameters
|
|
631
|
+
----------
|
|
632
|
+
positions : (N, 3) float32 device array
|
|
633
|
+
Points to compute signed distance for
|
|
634
|
+
signed_dist_out : (N,) float32 device array
|
|
635
|
+
Output array for signed distances
|
|
636
|
+
geometry_id : int
|
|
637
|
+
Surface geometry type:
|
|
638
|
+
1 = Plane
|
|
639
|
+
2 = Sphere
|
|
640
|
+
3 = Gerstner wave (flat earth)
|
|
641
|
+
4 = Curved wave (spherical earth)
|
|
642
|
+
5 = Multi-wave curved (up to 8 waves)
|
|
643
|
+
params : (64,) float32 device array
|
|
644
|
+
Surface parameters from surface.get_gpu_parameters()
|
|
645
|
+
"""
|
|
646
|
+
idx = cuda.grid(1)
|
|
647
|
+
if idx >= positions.shape[0]:
|
|
648
|
+
return
|
|
649
|
+
|
|
650
|
+
x = positions[idx, 0]
|
|
651
|
+
y = positions[idx, 1]
|
|
652
|
+
z = positions[idx, 2]
|
|
653
|
+
|
|
654
|
+
if geometry_id == 1:
|
|
655
|
+
signed_dist_out[idx] = _device_plane_sd(x, y, z, params)
|
|
656
|
+
elif geometry_id == 2:
|
|
657
|
+
signed_dist_out[idx] = _device_sphere_sd(x, y, z, params)
|
|
658
|
+
elif geometry_id == 3:
|
|
659
|
+
signed_dist_out[idx] = _device_gerstner_sd(x, y, z, params)
|
|
660
|
+
elif geometry_id == 4:
|
|
661
|
+
signed_dist_out[idx] = _device_curved_wave_sd(x, y, z, params)
|
|
662
|
+
elif geometry_id == 5:
|
|
663
|
+
signed_dist_out[idx] = _device_multi_curved_wave_sd(x, y, z, params)
|
|
664
|
+
elif geometry_id == 6:
|
|
665
|
+
# Bounded plane uses same SD formula as infinite plane
|
|
666
|
+
signed_dist_out[idx] = _device_plane_sd(x, y, z, params)
|
|
667
|
+
elif geometry_id == 7:
|
|
668
|
+
signed_dist_out[idx] = _device_annular_plane_sd(x, y, z, params)
|
|
669
|
+
else:
|
|
670
|
+
# Unknown geometry - set to inf (no intersection possible)
|
|
671
|
+
signed_dist_out[idx] = math.inf
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
@register_kernel(IntersectionKernelID.SURFACE_NORMAL_GENERIC)
|
|
675
|
+
@cuda.jit
|
|
676
|
+
def kernel_surface_normal(
|
|
677
|
+
positions, # (N, 3) input positions
|
|
678
|
+
normals_out, # (N, 3) output normals
|
|
679
|
+
geometry_id, # int: 1=plane, 2=sphere, 3=gerstner, 4=curved_wave, 5=multi_curved_wave
|
|
680
|
+
params, # (64,) surface parameters
|
|
681
|
+
):
|
|
682
|
+
"""
|
|
683
|
+
CUDA kernel to compute surface normal at given positions.
|
|
684
|
+
|
|
685
|
+
Dispatches to appropriate device function based on geometry_id.
|
|
686
|
+
Used after intersection detection to compute normals for Fresnel/reflection.
|
|
687
|
+
|
|
688
|
+
Parameters
|
|
689
|
+
----------
|
|
690
|
+
positions : (N, 3) float32 device array
|
|
691
|
+
Points on the surface (or close to it)
|
|
692
|
+
normals_out : (N, 3) float32 device array
|
|
693
|
+
Output array for normal vectors
|
|
694
|
+
geometry_id : int
|
|
695
|
+
Surface geometry type (1-5)
|
|
696
|
+
params : (64,) float32 device array
|
|
697
|
+
Surface parameters from surface.get_gpu_parameters()
|
|
698
|
+
"""
|
|
699
|
+
idx = cuda.grid(1)
|
|
700
|
+
if idx >= positions.shape[0]:
|
|
701
|
+
return
|
|
702
|
+
|
|
703
|
+
x = positions[idx, 0]
|
|
704
|
+
y = positions[idx, 1]
|
|
705
|
+
z = positions[idx, 2]
|
|
706
|
+
|
|
707
|
+
# Placeholder for output (not used by device functions, just for signature)
|
|
708
|
+
out_nx, out_ny, out_nz = 0.0, 0.0, 0.0
|
|
709
|
+
|
|
710
|
+
if geometry_id == 1:
|
|
711
|
+
nx, ny, nz = _device_plane_normal(x, y, z, params, out_nx, out_ny, out_nz)
|
|
712
|
+
elif geometry_id == 2:
|
|
713
|
+
nx, ny, nz = _device_sphere_normal(x, y, z, params, out_nx, out_ny, out_nz)
|
|
714
|
+
elif geometry_id == 3:
|
|
715
|
+
nx, ny, nz = _device_gerstner_normal(x, y, z, params, out_nx, out_ny, out_nz)
|
|
716
|
+
elif geometry_id == 4:
|
|
717
|
+
nx, ny, nz = _device_curved_wave_normal(x, y, z, params, out_nx, out_ny, out_nz)
|
|
718
|
+
elif geometry_id == 5:
|
|
719
|
+
nx, ny, nz = _device_multi_curved_wave_normal(
|
|
720
|
+
x, y, z, params, out_nx, out_ny, out_nz
|
|
721
|
+
)
|
|
722
|
+
elif geometry_id == 6:
|
|
723
|
+
# Bounded plane uses same normal as infinite plane
|
|
724
|
+
nx, ny, nz = _device_plane_normal(x, y, z, params, out_nx, out_ny, out_nz)
|
|
725
|
+
elif geometry_id == 7:
|
|
726
|
+
nx, ny, nz = _device_annular_plane_normal(
|
|
727
|
+
x, y, z, params, out_nx, out_ny, out_nz
|
|
728
|
+
)
|
|
729
|
+
else:
|
|
730
|
+
# Unknown geometry - default to +Z normal
|
|
731
|
+
nx, ny, nz = 0.0, 0.0, 1.0
|
|
732
|
+
|
|
733
|
+
normals_out[idx, 0] = nx
|
|
734
|
+
normals_out[idx, 1] = ny
|
|
735
|
+
normals_out[idx, 2] = nz
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
__all__ = [
|
|
739
|
+
"kernel_signed_distance",
|
|
740
|
+
"kernel_surface_normal",
|
|
741
|
+
"HAS_CUDA",
|
|
742
|
+
]
|