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,64 @@
|
|
|
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
|
+
Fresnel Kernels
|
|
36
|
+
|
|
37
|
+
CUDA kernels for GPU-accelerated Fresnel reflection/refraction calculations.
|
|
38
|
+
Computes reflection and transmission coefficients at material interfaces.
|
|
39
|
+
|
|
40
|
+
This module exports ONLY pure CUDA kernels and device functions.
|
|
41
|
+
GPU memory management is handled by the propagator layer (handlers).
|
|
42
|
+
|
|
43
|
+
Kernel Types
|
|
44
|
+
------------
|
|
45
|
+
- STANDARD: Standard Fresnel equations for unpolarized light
|
|
46
|
+
- POLARIZED: Polarization-aware Fresnel equations (s and p separately)
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from .protocol import FresnelKernelProtocol
|
|
50
|
+
from .standard import (
|
|
51
|
+
kernel_fresnel_standard,
|
|
52
|
+
kernel_fresnel_polarized,
|
|
53
|
+
HAS_CUDA,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
# Protocol
|
|
58
|
+
"FresnelKernelProtocol",
|
|
59
|
+
# Kernels (pure CUDA only)
|
|
60
|
+
"kernel_fresnel_standard",
|
|
61
|
+
"kernel_fresnel_polarized",
|
|
62
|
+
# CUDA availability
|
|
63
|
+
"HAS_CUDA",
|
|
64
|
+
]
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
Fresnel Kernel Protocol
|
|
36
|
+
|
|
37
|
+
Defines the interface for Fresnel reflection/refraction kernels that
|
|
38
|
+
compute reflection and transmission coefficients at interfaces.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
from __future__ import annotations
|
|
42
|
+
|
|
43
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from numba.cuda.devicearray import DeviceNDArray
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@runtime_checkable
|
|
50
|
+
class FresnelKernelProtocol(Protocol):
|
|
51
|
+
"""
|
|
52
|
+
Protocol for Fresnel reflection/refraction kernels.
|
|
53
|
+
|
|
54
|
+
Fresnel kernels compute reflection and transmission coefficients
|
|
55
|
+
at material interfaces based on incident angle, refractive indices,
|
|
56
|
+
and polarization state.
|
|
57
|
+
|
|
58
|
+
Common Parameters
|
|
59
|
+
-----------------
|
|
60
|
+
cos_theta_i : DeviceNDArray, shape (N,)
|
|
61
|
+
Cosine of incident angle (dot product of direction and normal)
|
|
62
|
+
n1 : DeviceNDArray or float
|
|
63
|
+
Refractive index of incident medium
|
|
64
|
+
n2 : DeviceNDArray or float
|
|
65
|
+
Refractive index of transmitted medium
|
|
66
|
+
reflectance_out : DeviceNDArray, shape (N,)
|
|
67
|
+
Output: reflection coefficient (0-1)
|
|
68
|
+
transmittance_out : DeviceNDArray, shape (N,)
|
|
69
|
+
Output: transmission coefficient (0-1)
|
|
70
|
+
|
|
71
|
+
Optional Parameters
|
|
72
|
+
-------------------
|
|
73
|
+
polarization : DeviceNDArray, shape (N,), optional
|
|
74
|
+
Polarization state per ray (0=unpolarized, 1=s, 2=p)
|
|
75
|
+
|
|
76
|
+
Notes
|
|
77
|
+
-----
|
|
78
|
+
Kernels are registered with the central registry using @register_kernel
|
|
79
|
+
decorator and FresnelKernelID enum values.
|
|
80
|
+
|
|
81
|
+
For unpolarized light, the kernel averages s and p polarizations.
|
|
82
|
+
Total internal reflection (TIR) is automatically handled when
|
|
83
|
+
sin(theta_t) > 1.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __call__(
|
|
87
|
+
self,
|
|
88
|
+
cos_theta_i: DeviceNDArray,
|
|
89
|
+
n1: DeviceNDArray | float,
|
|
90
|
+
n2: DeviceNDArray | float,
|
|
91
|
+
reflectance_out: DeviceNDArray,
|
|
92
|
+
transmittance_out: DeviceNDArray,
|
|
93
|
+
*args,
|
|
94
|
+
**kwargs,
|
|
95
|
+
) -> None:
|
|
96
|
+
"""Execute the Fresnel kernel."""
|
|
97
|
+
...
|
|
@@ -0,0 +1,258 @@
|
|
|
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-Accelerated Fresnel Equations
|
|
36
|
+
|
|
37
|
+
Computes reflection and transmission coefficients at interfaces between
|
|
38
|
+
different media using Fresnel equations.
|
|
39
|
+
|
|
40
|
+
This module contains ONLY pure CUDA kernels and device functions - no GPU memory
|
|
41
|
+
management. The propagator layer is responsible for cuda.to_device() and copy_to_host().
|
|
42
|
+
|
|
43
|
+
References
|
|
44
|
+
----------
|
|
45
|
+
.. [1] Born, M., & Wolf, E. (1999). Principles of Optics (7th ed.).
|
|
46
|
+
Cambridge University Press. Chapter 1.5.
|
|
47
|
+
.. [2] Hecht, E. (2017). Optics (5th ed.). Pearson. Chapter 4.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
import math
|
|
51
|
+
|
|
52
|
+
from ..registry import FresnelKernelID, register_kernel
|
|
53
|
+
|
|
54
|
+
# GPU support is optional
|
|
55
|
+
try:
|
|
56
|
+
from numba import cuda
|
|
57
|
+
|
|
58
|
+
HAS_CUDA = True
|
|
59
|
+
except ImportError:
|
|
60
|
+
|
|
61
|
+
class _FakeCuda:
|
|
62
|
+
"""Fake cuda module for when numba is not installed."""
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def jit(*args, **kwargs):
|
|
66
|
+
"""Return a no-op decorator."""
|
|
67
|
+
|
|
68
|
+
def decorator(func):
|
|
69
|
+
return func
|
|
70
|
+
|
|
71
|
+
if args and callable(args[0]):
|
|
72
|
+
return args[0]
|
|
73
|
+
return decorator
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def is_available():
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def grid(n):
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def synchronize():
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
cuda = _FakeCuda() # type: ignore[assignment]
|
|
88
|
+
HAS_CUDA = False
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# =============================================================================
|
|
92
|
+
# CUDA Device Functions
|
|
93
|
+
# =============================================================================
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@cuda.jit(device=True)
|
|
97
|
+
def _device_fresnel_rs_rp(
|
|
98
|
+
cos_theta_i: float,
|
|
99
|
+
n1: float,
|
|
100
|
+
n2: float,
|
|
101
|
+
) -> tuple[float, float, float, bool]:
|
|
102
|
+
"""
|
|
103
|
+
Compute Fresnel amplitude reflection coefficients r_s and r_p.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
cos_theta_i : float
|
|
108
|
+
Cosine of incident angle
|
|
109
|
+
n1 : float
|
|
110
|
+
Refractive index of incident medium
|
|
111
|
+
n2 : float
|
|
112
|
+
Refractive index of transmitted medium
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
r_s : float
|
|
117
|
+
s-polarization amplitude reflection coefficient
|
|
118
|
+
r_p : float
|
|
119
|
+
p-polarization amplitude reflection coefficient
|
|
120
|
+
cos_theta_t : float
|
|
121
|
+
Cosine of transmitted angle
|
|
122
|
+
tir : bool
|
|
123
|
+
True if total internal reflection
|
|
124
|
+
"""
|
|
125
|
+
# Snell's law: n1*sin(theta_i) = n2*sin(theta_t)
|
|
126
|
+
n_ratio = n1 / n2
|
|
127
|
+
sin_theta_i_sq = 1.0 - cos_theta_i * cos_theta_i
|
|
128
|
+
sin_theta_t_sq = n_ratio * n_ratio * sin_theta_i_sq
|
|
129
|
+
|
|
130
|
+
# Check for total internal reflection
|
|
131
|
+
if sin_theta_t_sq > 1.0:
|
|
132
|
+
return 1.0, 1.0, 0.0, True
|
|
133
|
+
|
|
134
|
+
cos_theta_t = math.sqrt(1.0 - sin_theta_t_sq)
|
|
135
|
+
|
|
136
|
+
# Fresnel equations for amplitude coefficients
|
|
137
|
+
# s-polarization (TE)
|
|
138
|
+
r_s_num = n1 * cos_theta_i - n2 * cos_theta_t
|
|
139
|
+
r_s_den = n1 * cos_theta_i + n2 * cos_theta_t
|
|
140
|
+
r_s = r_s_num / (r_s_den + 1e-10)
|
|
141
|
+
|
|
142
|
+
# p-polarization (TM)
|
|
143
|
+
r_p_num = n2 * cos_theta_i - n1 * cos_theta_t
|
|
144
|
+
r_p_den = n2 * cos_theta_i + n1 * cos_theta_t
|
|
145
|
+
r_p = r_p_num / (r_p_den + 1e-10)
|
|
146
|
+
|
|
147
|
+
return r_s, r_p, cos_theta_t, False
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# =============================================================================
|
|
151
|
+
# CUDA Kernels
|
|
152
|
+
# =============================================================================
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@register_kernel(FresnelKernelID.STANDARD)
|
|
156
|
+
@cuda.jit
|
|
157
|
+
def kernel_fresnel_standard(
|
|
158
|
+
cos_theta_i, # (N,) cosine of incident angles
|
|
159
|
+
n1, # (N,) or scalar: refractive index of incident medium
|
|
160
|
+
n2, # (N,) or scalar: refractive index of transmitted medium
|
|
161
|
+
reflectance_out, # (N,) output: reflection coefficient
|
|
162
|
+
transmittance_out, # (N,) output: transmission coefficient
|
|
163
|
+
n1_is_scalar, # bool: whether n1 is scalar
|
|
164
|
+
n2_is_scalar, # bool: whether n2 is scalar
|
|
165
|
+
):
|
|
166
|
+
"""
|
|
167
|
+
CUDA kernel for standard Fresnel equations (unpolarized light).
|
|
168
|
+
|
|
169
|
+
Computes reflection and transmission coefficients by averaging
|
|
170
|
+
s and p polarizations.
|
|
171
|
+
"""
|
|
172
|
+
idx = cuda.grid(1)
|
|
173
|
+
if idx >= cos_theta_i.shape[0]:
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
cti = cos_theta_i[idx]
|
|
177
|
+
if n1_is_scalar:
|
|
178
|
+
ni1 = n1[0]
|
|
179
|
+
else:
|
|
180
|
+
ni1 = n1[idx]
|
|
181
|
+
if n2_is_scalar:
|
|
182
|
+
ni2 = n2[0]
|
|
183
|
+
else:
|
|
184
|
+
ni2 = n2[idx]
|
|
185
|
+
|
|
186
|
+
r_s, r_p, cos_theta_t, tir = _device_fresnel_rs_rp(cti, ni1, ni2)
|
|
187
|
+
|
|
188
|
+
if tir:
|
|
189
|
+
reflectance_out[idx] = 1.0
|
|
190
|
+
transmittance_out[idx] = 0.0
|
|
191
|
+
else:
|
|
192
|
+
# Intensity reflection coefficients
|
|
193
|
+
R_s = r_s * r_s
|
|
194
|
+
R_p = r_p * r_p
|
|
195
|
+
|
|
196
|
+
# Average for unpolarized light
|
|
197
|
+
R = 0.5 * (R_s + R_p)
|
|
198
|
+
T = 1.0 - R
|
|
199
|
+
|
|
200
|
+
reflectance_out[idx] = R
|
|
201
|
+
transmittance_out[idx] = T
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@register_kernel(FresnelKernelID.POLARIZED)
|
|
205
|
+
@cuda.jit
|
|
206
|
+
def kernel_fresnel_polarized(
|
|
207
|
+
cos_theta_i, # (N,) cosine of incident angles
|
|
208
|
+
n1, # (N,) or scalar: refractive index of incident medium
|
|
209
|
+
n2, # (N,) or scalar: refractive index of transmitted medium
|
|
210
|
+
polarization, # (N,) polarization state: 0=unpolarized, 1=s, 2=p
|
|
211
|
+
reflectance_out, # (N,) output: reflection coefficient
|
|
212
|
+
transmittance_out, # (N,) output: transmission coefficient
|
|
213
|
+
n1_is_scalar, # bool: whether n1 is scalar
|
|
214
|
+
n2_is_scalar, # bool: whether n2 is scalar
|
|
215
|
+
):
|
|
216
|
+
"""
|
|
217
|
+
CUDA kernel for polarization-aware Fresnel equations.
|
|
218
|
+
|
|
219
|
+
Computes reflection and transmission coefficients based on
|
|
220
|
+
the specified polarization state per ray.
|
|
221
|
+
"""
|
|
222
|
+
idx = cuda.grid(1)
|
|
223
|
+
if idx >= cos_theta_i.shape[0]:
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
cti = cos_theta_i[idx]
|
|
227
|
+
pol = polarization[idx]
|
|
228
|
+
if n1_is_scalar:
|
|
229
|
+
ni1 = n1[0]
|
|
230
|
+
else:
|
|
231
|
+
ni1 = n1[idx]
|
|
232
|
+
if n2_is_scalar:
|
|
233
|
+
ni2 = n2[0]
|
|
234
|
+
else:
|
|
235
|
+
ni2 = n2[idx]
|
|
236
|
+
|
|
237
|
+
r_s, r_p, cos_theta_t, tir = _device_fresnel_rs_rp(cti, ni1, ni2)
|
|
238
|
+
|
|
239
|
+
if tir:
|
|
240
|
+
reflectance_out[idx] = 1.0
|
|
241
|
+
transmittance_out[idx] = 0.0
|
|
242
|
+
else:
|
|
243
|
+
# Intensity reflection coefficients
|
|
244
|
+
R_s = r_s * r_s
|
|
245
|
+
R_p = r_p * r_p
|
|
246
|
+
|
|
247
|
+
# Select based on polarization
|
|
248
|
+
if pol == 1: # s-polarization
|
|
249
|
+
R = R_s
|
|
250
|
+
elif pol == 2: # p-polarization
|
|
251
|
+
R = R_p
|
|
252
|
+
else: # unpolarized
|
|
253
|
+
R = 0.5 * (R_s + R_p)
|
|
254
|
+
|
|
255
|
+
T = 1.0 - R
|
|
256
|
+
|
|
257
|
+
reflectance_out[idx] = R
|
|
258
|
+
transmittance_out[idx] = T
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
Intersection Kernels
|
|
36
|
+
|
|
37
|
+
CUDA kernels for GPU-accelerated ray-surface intersection testing.
|
|
38
|
+
Supports analytical methods for simple geometries (plane, sphere, bounded plane)
|
|
39
|
+
and generic signed distance dispatch for all surface types.
|
|
40
|
+
|
|
41
|
+
This module exports ONLY pure CUDA kernels and device functions.
|
|
42
|
+
GPU memory management is handled by the propagator layer (handlers).
|
|
43
|
+
|
|
44
|
+
Kernel Types
|
|
45
|
+
------------
|
|
46
|
+
- Analytical: Fast closed-form solutions for simple geometries
|
|
47
|
+
- PLANE_ANALYTICAL: Infinite plane intersection
|
|
48
|
+
- SPHERE_ANALYTICAL: Sphere intersection
|
|
49
|
+
- BOUNDED_PLANE_ANALYTICAL: Bounded rectangular plane intersection
|
|
50
|
+
|
|
51
|
+
- Generic dispatch: GPU-accelerated signed distance for any surface
|
|
52
|
+
- SIGNED_DISTANCE_GENERIC: Signed distance via geometry_id dispatch
|
|
53
|
+
- SURFACE_NORMAL_GENERIC: Surface normal via geometry_id dispatch
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
from .protocol import IntersectionKernelProtocol
|
|
57
|
+
from .plane import kernel_plane_intersect, HAS_CUDA
|
|
58
|
+
from .sphere import kernel_sphere_intersect
|
|
59
|
+
from .bounded_plane import kernel_bounded_plane_intersect
|
|
60
|
+
from .annular_plane import kernel_annular_plane_intersect
|
|
61
|
+
from .signed_distance import kernel_signed_distance, kernel_surface_normal
|
|
62
|
+
|
|
63
|
+
__all__ = [
|
|
64
|
+
# Protocol
|
|
65
|
+
"IntersectionKernelProtocol",
|
|
66
|
+
# Plane intersection kernel
|
|
67
|
+
"kernel_plane_intersect",
|
|
68
|
+
# Sphere intersection kernel
|
|
69
|
+
"kernel_sphere_intersect",
|
|
70
|
+
# Bounded plane intersection kernel
|
|
71
|
+
"kernel_bounded_plane_intersect",
|
|
72
|
+
# Annular plane intersection kernel
|
|
73
|
+
"kernel_annular_plane_intersect",
|
|
74
|
+
# Generic signed distance kernels
|
|
75
|
+
"kernel_signed_distance",
|
|
76
|
+
"kernel_surface_normal",
|
|
77
|
+
# CUDA availability
|
|
78
|
+
"HAS_CUDA",
|
|
79
|
+
]
|
|
@@ -0,0 +1,207 @@
|
|
|
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-Accelerated Annular Plane Intersection
|
|
36
|
+
|
|
37
|
+
Analytical ray-plane intersection with annular (circular) bounds checking.
|
|
38
|
+
|
|
39
|
+
This module contains ONLY pure CUDA kernels - no GPU memory management.
|
|
40
|
+
The propagator layer is responsible for cuda.to_device() and copy_to_host().
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
import math
|
|
44
|
+
|
|
45
|
+
from ..registry import IntersectionKernelID, register_kernel
|
|
46
|
+
|
|
47
|
+
# GPU support is optional
|
|
48
|
+
try:
|
|
49
|
+
from numba import cuda
|
|
50
|
+
|
|
51
|
+
HAS_CUDA = True
|
|
52
|
+
except ImportError:
|
|
53
|
+
|
|
54
|
+
class _FakeCuda:
|
|
55
|
+
"""Fake cuda module for when numba is not installed."""
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def jit(*args, **kwargs):
|
|
59
|
+
"""Return a no-op decorator."""
|
|
60
|
+
|
|
61
|
+
def decorator(func):
|
|
62
|
+
return func
|
|
63
|
+
|
|
64
|
+
if args and callable(args[0]):
|
|
65
|
+
return args[0]
|
|
66
|
+
return decorator
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def is_available():
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def grid(n):
|
|
74
|
+
return 0
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def synchronize():
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
cuda = _FakeCuda() # type: ignore[assignment]
|
|
81
|
+
HAS_CUDA = False
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# =============================================================================
|
|
85
|
+
# CUDA Kernels
|
|
86
|
+
# =============================================================================
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@register_kernel(IntersectionKernelID.ANNULAR_PLANE_ANALYTICAL)
|
|
90
|
+
@cuda.jit
|
|
91
|
+
def kernel_annular_plane_intersect(
|
|
92
|
+
origins, # (N, 3) ray origins
|
|
93
|
+
directions, # (N, 3) ray directions
|
|
94
|
+
distances_out, # (N,) output distances
|
|
95
|
+
hit_mask_out, # (N,) output hit mask
|
|
96
|
+
normals_out, # (N, 3) output normals at intersection
|
|
97
|
+
# Plane parameters
|
|
98
|
+
plane_normal_x, # Plane normal X component
|
|
99
|
+
plane_normal_y, # Plane normal Y component
|
|
100
|
+
plane_normal_z, # Plane normal Z component
|
|
101
|
+
# Annulus center
|
|
102
|
+
center_x,
|
|
103
|
+
center_y,
|
|
104
|
+
center_z,
|
|
105
|
+
# Local U axis for coordinate computation
|
|
106
|
+
u_axis_x,
|
|
107
|
+
u_axis_y,
|
|
108
|
+
u_axis_z,
|
|
109
|
+
# Local V axis for coordinate computation
|
|
110
|
+
v_axis_x,
|
|
111
|
+
v_axis_y,
|
|
112
|
+
v_axis_z,
|
|
113
|
+
# Squared radii for annular bounds
|
|
114
|
+
inner_radius_sq,
|
|
115
|
+
outer_radius_sq,
|
|
116
|
+
):
|
|
117
|
+
"""
|
|
118
|
+
CUDA kernel for ray-annular-plane intersection.
|
|
119
|
+
|
|
120
|
+
Uses analytical solution for ray-plane intersection, then checks
|
|
121
|
+
if the intersection point lies within the annular bounds.
|
|
122
|
+
|
|
123
|
+
The annular plane is defined by its center point and normal.
|
|
124
|
+
The annular bounds are checked using the squared radial distance
|
|
125
|
+
from the center in the local coordinate frame.
|
|
126
|
+
"""
|
|
127
|
+
idx = cuda.grid(1)
|
|
128
|
+
if idx >= origins.shape[0]:
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# Load ray data
|
|
132
|
+
ox = origins[idx, 0]
|
|
133
|
+
oy = origins[idx, 1]
|
|
134
|
+
oz = origins[idx, 2]
|
|
135
|
+
dx = directions[idx, 0]
|
|
136
|
+
dy = directions[idx, 1]
|
|
137
|
+
dz = directions[idx, 2]
|
|
138
|
+
|
|
139
|
+
# Normalize direction
|
|
140
|
+
d_len = math.sqrt(dx * dx + dy * dy + dz * dz)
|
|
141
|
+
if d_len < 1e-10:
|
|
142
|
+
hit_mask_out[idx] = False
|
|
143
|
+
distances_out[idx] = math.inf
|
|
144
|
+
return
|
|
145
|
+
dx /= d_len
|
|
146
|
+
dy /= d_len
|
|
147
|
+
dz /= d_len
|
|
148
|
+
|
|
149
|
+
# Compute denominator: n . d
|
|
150
|
+
denom = plane_normal_x * dx + plane_normal_y * dy + plane_normal_z * dz
|
|
151
|
+
|
|
152
|
+
# Check if ray is parallel to plane
|
|
153
|
+
if abs(denom) < 1e-10:
|
|
154
|
+
hit_mask_out[idx] = False
|
|
155
|
+
distances_out[idx] = math.inf
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
# Compute numerator: (center - origin) . n
|
|
159
|
+
numer = (
|
|
160
|
+
plane_normal_x * (center_x - ox)
|
|
161
|
+
+ plane_normal_y * (center_y - oy)
|
|
162
|
+
+ plane_normal_z * (center_z - oz)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Compute intersection distance
|
|
166
|
+
t = numer / denom
|
|
167
|
+
|
|
168
|
+
# Only forward intersections
|
|
169
|
+
if t < 0:
|
|
170
|
+
hit_mask_out[idx] = False
|
|
171
|
+
distances_out[idx] = math.inf
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Compute intersection point
|
|
175
|
+
px = ox + t * dx
|
|
176
|
+
py = oy + t * dy
|
|
177
|
+
pz = oz + t * dz
|
|
178
|
+
|
|
179
|
+
# Vector from center to intersection
|
|
180
|
+
rx = px - center_x
|
|
181
|
+
ry = py - center_y
|
|
182
|
+
rz = pz - center_z
|
|
183
|
+
|
|
184
|
+
# Project onto local axes
|
|
185
|
+
u_coord = rx * u_axis_x + ry * u_axis_y + rz * u_axis_z
|
|
186
|
+
v_coord = rx * v_axis_x + ry * v_axis_y + rz * v_axis_z
|
|
187
|
+
|
|
188
|
+
# Check annular bounds (circular)
|
|
189
|
+
r_sq = u_coord * u_coord + v_coord * v_coord
|
|
190
|
+
if r_sq < inner_radius_sq or r_sq > outer_radius_sq:
|
|
191
|
+
hit_mask_out[idx] = False
|
|
192
|
+
distances_out[idx] = math.inf
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
# Valid annular intersection
|
|
196
|
+
hit_mask_out[idx] = True
|
|
197
|
+
distances_out[idx] = t
|
|
198
|
+
|
|
199
|
+
# Normal points toward ray origin (flip if ray hits from behind)
|
|
200
|
+
if denom > 0:
|
|
201
|
+
normals_out[idx, 0] = -plane_normal_x
|
|
202
|
+
normals_out[idx, 1] = -plane_normal_y
|
|
203
|
+
normals_out[idx, 2] = -plane_normal_z
|
|
204
|
+
else:
|
|
205
|
+
normals_out[idx, 0] = plane_normal_x
|
|
206
|
+
normals_out[idx, 1] = plane_normal_y
|
|
207
|
+
normals_out[idx, 2] = plane_normal_z
|