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.
Files changed (180) hide show
  1. lsurf/__init__.py +471 -0
  2. lsurf/analysis/__init__.py +107 -0
  3. lsurf/analysis/healpix_utils.py +418 -0
  4. lsurf/analysis/sphere_viz.py +1280 -0
  5. lsurf/cli/__init__.py +48 -0
  6. lsurf/cli/build.py +398 -0
  7. lsurf/cli/config_schema.py +318 -0
  8. lsurf/cli/gui_cmd.py +76 -0
  9. lsurf/cli/interactive.py +850 -0
  10. lsurf/cli/main.py +81 -0
  11. lsurf/cli/run.py +806 -0
  12. lsurf/detectors/__init__.py +266 -0
  13. lsurf/detectors/analysis.py +289 -0
  14. lsurf/detectors/base.py +284 -0
  15. lsurf/detectors/constant_size_rings.py +485 -0
  16. lsurf/detectors/directional.py +45 -0
  17. lsurf/detectors/extended/__init__.py +73 -0
  18. lsurf/detectors/extended/local_sphere.py +353 -0
  19. lsurf/detectors/extended/recording_sphere.py +368 -0
  20. lsurf/detectors/planar.py +45 -0
  21. lsurf/detectors/protocol.py +187 -0
  22. lsurf/detectors/recording_spheres.py +63 -0
  23. lsurf/detectors/results.py +1140 -0
  24. lsurf/detectors/small/__init__.py +79 -0
  25. lsurf/detectors/small/directional.py +330 -0
  26. lsurf/detectors/small/planar.py +401 -0
  27. lsurf/detectors/small/spherical.py +450 -0
  28. lsurf/detectors/spherical.py +45 -0
  29. lsurf/geometry/__init__.py +199 -0
  30. lsurf/geometry/builder.py +478 -0
  31. lsurf/geometry/cell.py +228 -0
  32. lsurf/geometry/cell_geometry.py +247 -0
  33. lsurf/geometry/detector_arrays.py +1785 -0
  34. lsurf/geometry/geometry.py +222 -0
  35. lsurf/geometry/surface_analysis.py +375 -0
  36. lsurf/geometry/validation.py +91 -0
  37. lsurf/gui/__init__.py +51 -0
  38. lsurf/gui/app.py +903 -0
  39. lsurf/gui/core/__init__.py +39 -0
  40. lsurf/gui/core/scene.py +343 -0
  41. lsurf/gui/core/simulation.py +264 -0
  42. lsurf/gui/renderers/__init__.py +40 -0
  43. lsurf/gui/renderers/ray_renderer.py +353 -0
  44. lsurf/gui/renderers/source_renderer.py +505 -0
  45. lsurf/gui/renderers/surface_renderer.py +477 -0
  46. lsurf/gui/views/__init__.py +48 -0
  47. lsurf/gui/views/config_editor.py +3199 -0
  48. lsurf/gui/views/properties.py +257 -0
  49. lsurf/gui/views/results.py +291 -0
  50. lsurf/gui/views/scene_tree.py +180 -0
  51. lsurf/gui/views/viewport_3d.py +555 -0
  52. lsurf/gui/views/visualizations.py +712 -0
  53. lsurf/materials/__init__.py +169 -0
  54. lsurf/materials/base/__init__.py +64 -0
  55. lsurf/materials/base/full_inhomogeneous.py +208 -0
  56. lsurf/materials/base/grid_inhomogeneous.py +319 -0
  57. lsurf/materials/base/homogeneous.py +342 -0
  58. lsurf/materials/base/material_field.py +527 -0
  59. lsurf/materials/base/simple_inhomogeneous.py +418 -0
  60. lsurf/materials/base/spectral_inhomogeneous.py +497 -0
  61. lsurf/materials/implementations/__init__.py +120 -0
  62. lsurf/materials/implementations/data/alpha_values_typical_atmosphere_updated.txt +24 -0
  63. lsurf/materials/implementations/duct_atmosphere.py +390 -0
  64. lsurf/materials/implementations/exponential_atmosphere.py +435 -0
  65. lsurf/materials/implementations/gaussian_lens.py +120 -0
  66. lsurf/materials/implementations/interpolated_data.py +123 -0
  67. lsurf/materials/implementations/layered_atmosphere.py +134 -0
  68. lsurf/materials/implementations/linear_gradient.py +109 -0
  69. lsurf/materials/implementations/linsley_atmosphere.py +764 -0
  70. lsurf/materials/implementations/standard_materials.py +126 -0
  71. lsurf/materials/implementations/turbulent_atmosphere.py +135 -0
  72. lsurf/materials/implementations/us_standard_atmosphere.py +149 -0
  73. lsurf/materials/utils/__init__.py +77 -0
  74. lsurf/materials/utils/constants.py +45 -0
  75. lsurf/materials/utils/device_functions.py +117 -0
  76. lsurf/materials/utils/dispersion.py +160 -0
  77. lsurf/materials/utils/factories.py +142 -0
  78. lsurf/propagation/__init__.py +91 -0
  79. lsurf/propagation/detector_gpu.py +67 -0
  80. lsurf/propagation/gpu_device_rays.py +294 -0
  81. lsurf/propagation/kernels/__init__.py +175 -0
  82. lsurf/propagation/kernels/absorption/__init__.py +61 -0
  83. lsurf/propagation/kernels/absorption/grid.py +240 -0
  84. lsurf/propagation/kernels/absorption/simple.py +232 -0
  85. lsurf/propagation/kernels/absorption/spectral.py +410 -0
  86. lsurf/propagation/kernels/detection/__init__.py +64 -0
  87. lsurf/propagation/kernels/detection/protocol.py +102 -0
  88. lsurf/propagation/kernels/detection/spherical.py +255 -0
  89. lsurf/propagation/kernels/device_functions.py +790 -0
  90. lsurf/propagation/kernels/fresnel/__init__.py +64 -0
  91. lsurf/propagation/kernels/fresnel/protocol.py +97 -0
  92. lsurf/propagation/kernels/fresnel/standard.py +258 -0
  93. lsurf/propagation/kernels/intersection/__init__.py +79 -0
  94. lsurf/propagation/kernels/intersection/annular_plane.py +207 -0
  95. lsurf/propagation/kernels/intersection/bounded_plane.py +205 -0
  96. lsurf/propagation/kernels/intersection/plane.py +166 -0
  97. lsurf/propagation/kernels/intersection/protocol.py +95 -0
  98. lsurf/propagation/kernels/intersection/signed_distance.py +742 -0
  99. lsurf/propagation/kernels/intersection/sphere.py +190 -0
  100. lsurf/propagation/kernels/propagation/__init__.py +85 -0
  101. lsurf/propagation/kernels/propagation/grid.py +527 -0
  102. lsurf/propagation/kernels/propagation/protocol.py +105 -0
  103. lsurf/propagation/kernels/propagation/simple.py +460 -0
  104. lsurf/propagation/kernels/propagation/spectral.py +875 -0
  105. lsurf/propagation/kernels/registry.py +331 -0
  106. lsurf/propagation/kernels/surface/__init__.py +72 -0
  107. lsurf/propagation/kernels/surface/bisection.py +232 -0
  108. lsurf/propagation/kernels/surface/detection.py +402 -0
  109. lsurf/propagation/kernels/surface/reduction.py +166 -0
  110. lsurf/propagation/propagator_protocol.py +222 -0
  111. lsurf/propagation/propagators/__init__.py +101 -0
  112. lsurf/propagation/propagators/detector_handler.py +354 -0
  113. lsurf/propagation/propagators/factory.py +200 -0
  114. lsurf/propagation/propagators/fresnel_handler.py +305 -0
  115. lsurf/propagation/propagators/gpu_gradient.py +566 -0
  116. lsurf/propagation/propagators/gpu_surface_propagator.py +707 -0
  117. lsurf/propagation/propagators/gradient.py +429 -0
  118. lsurf/propagation/propagators/intersection_handler.py +327 -0
  119. lsurf/propagation/propagators/material_propagator.py +398 -0
  120. lsurf/propagation/propagators/signed_distance_handler.py +522 -0
  121. lsurf/propagation/propagators/spectral_gpu_gradient.py +553 -0
  122. lsurf/propagation/propagators/surface_interaction.py +616 -0
  123. lsurf/propagation/propagators/surface_propagator.py +719 -0
  124. lsurf/py.typed +1 -0
  125. lsurf/simulation/__init__.py +70 -0
  126. lsurf/simulation/config.py +164 -0
  127. lsurf/simulation/orchestrator.py +462 -0
  128. lsurf/simulation/result.py +299 -0
  129. lsurf/simulation/simulation.py +262 -0
  130. lsurf/sources/__init__.py +128 -0
  131. lsurf/sources/base.py +264 -0
  132. lsurf/sources/collimated.py +252 -0
  133. lsurf/sources/custom.py +409 -0
  134. lsurf/sources/diverging.py +228 -0
  135. lsurf/sources/gaussian.py +272 -0
  136. lsurf/sources/parallel_from_positions.py +197 -0
  137. lsurf/sources/point.py +172 -0
  138. lsurf/sources/uniform_diverging.py +258 -0
  139. lsurf/surfaces/__init__.py +184 -0
  140. lsurf/surfaces/cpu/__init__.py +50 -0
  141. lsurf/surfaces/cpu/curved_wave.py +463 -0
  142. lsurf/surfaces/cpu/gerstner_wave.py +381 -0
  143. lsurf/surfaces/cpu/wave_params.py +118 -0
  144. lsurf/surfaces/gpu/__init__.py +72 -0
  145. lsurf/surfaces/gpu/annular_plane.py +453 -0
  146. lsurf/surfaces/gpu/bounded_plane.py +390 -0
  147. lsurf/surfaces/gpu/curved_wave.py +483 -0
  148. lsurf/surfaces/gpu/gerstner_wave.py +377 -0
  149. lsurf/surfaces/gpu/multi_curved_wave.py +520 -0
  150. lsurf/surfaces/gpu/plane.py +299 -0
  151. lsurf/surfaces/gpu/recording_sphere.py +587 -0
  152. lsurf/surfaces/gpu/sphere.py +311 -0
  153. lsurf/surfaces/protocol.py +336 -0
  154. lsurf/surfaces/registry.py +373 -0
  155. lsurf/utilities/__init__.py +175 -0
  156. lsurf/utilities/detector_analysis.py +814 -0
  157. lsurf/utilities/fresnel.py +628 -0
  158. lsurf/utilities/interactions.py +1215 -0
  159. lsurf/utilities/propagation.py +602 -0
  160. lsurf/utilities/ray_data.py +532 -0
  161. lsurf/utilities/recording_sphere.py +745 -0
  162. lsurf/utilities/time_spread.py +463 -0
  163. lsurf/visualization/__init__.py +329 -0
  164. lsurf/visualization/absorption_plots.py +334 -0
  165. lsurf/visualization/atmospheric_plots.py +754 -0
  166. lsurf/visualization/common.py +348 -0
  167. lsurf/visualization/detector_plots.py +1350 -0
  168. lsurf/visualization/detector_sphere_plots.py +1173 -0
  169. lsurf/visualization/fresnel_plots.py +1061 -0
  170. lsurf/visualization/ocean_simulation_plots.py +999 -0
  171. lsurf/visualization/polarization_plots.py +916 -0
  172. lsurf/visualization/raytracing_plots.py +1521 -0
  173. lsurf/visualization/ring_detector_plots.py +1867 -0
  174. lsurf/visualization/time_spread_plots.py +531 -0
  175. lsurf-1.0.0.dist-info/METADATA +381 -0
  176. lsurf-1.0.0.dist-info/RECORD +180 -0
  177. lsurf-1.0.0.dist-info/WHEEL +5 -0
  178. lsurf-1.0.0.dist-info/entry_points.txt +2 -0
  179. lsurf-1.0.0.dist-info/licenses/LICENSE +32 -0
  180. lsurf-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,205 @@
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 Bounded Plane Intersection
36
+
37
+ Analytical ray-plane intersection with rectangular 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.BOUNDED_PLANE_ANALYTICAL)
90
+ @cuda.jit
91
+ def kernel_bounded_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
+ # Rectangle center
102
+ center_x,
103
+ center_y,
104
+ center_z,
105
+ # Local U axis for bounds checking
106
+ u_axis_x,
107
+ u_axis_y,
108
+ u_axis_z,
109
+ # Local V axis for bounds checking
110
+ v_axis_x,
111
+ v_axis_y,
112
+ v_axis_z,
113
+ # Half-dimensions
114
+ half_width,
115
+ half_height,
116
+ ):
117
+ """
118
+ CUDA kernel for ray-bounded-plane intersection.
119
+
120
+ Uses analytical solution for ray-plane intersection, then checks
121
+ if the intersection point lies within the rectangular bounds.
122
+
123
+ The plane is defined by its center point and normal.
124
+ The rectangle bounds are checked by projecting onto local U/V axes.
125
+ """
126
+ idx = cuda.grid(1)
127
+ if idx >= origins.shape[0]:
128
+ return
129
+
130
+ # Load ray data
131
+ ox = origins[idx, 0]
132
+ oy = origins[idx, 1]
133
+ oz = origins[idx, 2]
134
+ dx = directions[idx, 0]
135
+ dy = directions[idx, 1]
136
+ dz = directions[idx, 2]
137
+
138
+ # Normalize direction
139
+ d_len = math.sqrt(dx * dx + dy * dy + dz * dz)
140
+ if d_len < 1e-10:
141
+ hit_mask_out[idx] = False
142
+ distances_out[idx] = math.inf
143
+ return
144
+ dx /= d_len
145
+ dy /= d_len
146
+ dz /= d_len
147
+
148
+ # Compute denominator: n · d
149
+ denom = plane_normal_x * dx + plane_normal_y * dy + plane_normal_z * dz
150
+
151
+ # Check if ray is parallel to plane
152
+ if abs(denom) < 1e-10:
153
+ hit_mask_out[idx] = False
154
+ distances_out[idx] = math.inf
155
+ return
156
+
157
+ # Compute numerator: (center - origin) · n
158
+ numer = (
159
+ plane_normal_x * (center_x - ox)
160
+ + plane_normal_y * (center_y - oy)
161
+ + plane_normal_z * (center_z - oz)
162
+ )
163
+
164
+ # Compute intersection distance
165
+ t = numer / denom
166
+
167
+ # Only forward intersections
168
+ if t < 0:
169
+ hit_mask_out[idx] = False
170
+ distances_out[idx] = math.inf
171
+ return
172
+
173
+ # Compute intersection point
174
+ px = ox + t * dx
175
+ py = oy + t * dy
176
+ pz = oz + t * dz
177
+
178
+ # Vector from center to intersection
179
+ rx = px - center_x
180
+ ry = py - center_y
181
+ rz = pz - center_z
182
+
183
+ # Project onto local axes
184
+ u_coord = rx * u_axis_x + ry * u_axis_y + rz * u_axis_z
185
+ v_coord = rx * v_axis_x + ry * v_axis_y + rz * v_axis_z
186
+
187
+ # Check bounds
188
+ if abs(u_coord) > half_width or abs(v_coord) > half_height:
189
+ hit_mask_out[idx] = False
190
+ distances_out[idx] = math.inf
191
+ return
192
+
193
+ # Valid bounded intersection
194
+ hit_mask_out[idx] = True
195
+ distances_out[idx] = t
196
+
197
+ # Normal points toward ray origin (flip if ray hits from behind)
198
+ if denom > 0:
199
+ normals_out[idx, 0] = -plane_normal_x
200
+ normals_out[idx, 1] = -plane_normal_y
201
+ normals_out[idx, 2] = -plane_normal_z
202
+ else:
203
+ normals_out[idx, 0] = plane_normal_x
204
+ normals_out[idx, 1] = plane_normal_y
205
+ normals_out[idx, 2] = plane_normal_z
@@ -0,0 +1,166 @@
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 Plane Intersection
36
+
37
+ Analytical ray-plane intersection using signed distance.
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.PLANE_ANALYTICAL)
90
+ @cuda.jit
91
+ def kernel_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_normal_x, # Plane normal X component
98
+ plane_normal_y, # Plane normal Y component
99
+ plane_normal_z, # Plane normal Z component
100
+ plane_d, # Plane distance from origin (ax + by + cz + d = 0)
101
+ ):
102
+ """
103
+ CUDA kernel for ray-plane intersection.
104
+
105
+ Uses analytical solution: t = -(n·o + d) / (n·d)
106
+ where n is plane normal, o is ray origin, d is ray direction.
107
+
108
+ The plane is defined as: n·p + d = 0
109
+ where n is the normal and d is the signed distance from origin.
110
+ """
111
+ idx = cuda.grid(1)
112
+ if idx >= origins.shape[0]:
113
+ return
114
+
115
+ # Load ray data
116
+ ox = origins[idx, 0]
117
+ oy = origins[idx, 1]
118
+ oz = origins[idx, 2]
119
+ dx = directions[idx, 0]
120
+ dy = directions[idx, 1]
121
+ dz = directions[idx, 2]
122
+
123
+ # Normalize direction
124
+ d_len = math.sqrt(dx * dx + dy * dy + dz * dz)
125
+ if d_len < 1e-10:
126
+ hit_mask_out[idx] = False
127
+ distances_out[idx] = math.inf
128
+ return
129
+ dx /= d_len
130
+ dy /= d_len
131
+ dz /= d_len
132
+
133
+ # Compute denominator: n · d
134
+ denom = plane_normal_x * dx + plane_normal_y * dy + plane_normal_z * dz
135
+
136
+ # Check if ray is parallel to plane
137
+ if abs(denom) < 1e-10:
138
+ hit_mask_out[idx] = False
139
+ distances_out[idx] = math.inf
140
+ return
141
+
142
+ # Compute numerator: -(n · o + d)
143
+ numer = -(plane_normal_x * ox + plane_normal_y * oy + plane_normal_z * oz + plane_d)
144
+
145
+ # Compute intersection distance
146
+ t = numer / denom
147
+
148
+ # Only forward intersections
149
+ if t < 0:
150
+ hit_mask_out[idx] = False
151
+ distances_out[idx] = math.inf
152
+ return
153
+
154
+ # Store results
155
+ hit_mask_out[idx] = True
156
+ distances_out[idx] = t
157
+
158
+ # Normal points toward ray origin (flip if ray hits from behind)
159
+ if denom > 0:
160
+ normals_out[idx, 0] = -plane_normal_x
161
+ normals_out[idx, 1] = -plane_normal_y
162
+ normals_out[idx, 2] = -plane_normal_z
163
+ else:
164
+ normals_out[idx, 0] = plane_normal_x
165
+ normals_out[idx, 1] = plane_normal_y
166
+ normals_out[idx, 2] = plane_normal_z
@@ -0,0 +1,95 @@
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 Kernel Protocol
36
+
37
+ Defines the interface for surface intersection kernels that find
38
+ ray-surface intersections.
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 IntersectionKernelProtocol(Protocol):
51
+ """
52
+ Protocol for surface intersection kernels.
53
+
54
+ Intersection kernels find where rays intersect with geometric surfaces.
55
+ They support different geometry types (planes, spheres, waves) and
56
+ intersection methods (analytical, ray-marching).
57
+
58
+ Common Parameters
59
+ -----------------
60
+ origins : DeviceNDArray, shape (N, 3)
61
+ Ray origin positions in meters
62
+ directions : DeviceNDArray, shape (N, 3)
63
+ Ray direction unit vectors
64
+ distances_out : DeviceNDArray, shape (N,)
65
+ Output: distance to intersection (inf if no hit)
66
+ hit_mask_out : DeviceNDArray, shape (N,)
67
+ Output: boolean mask of rays that hit
68
+ normals_out : DeviceNDArray, shape (N, 3)
69
+ Output: surface normals at intersection points
70
+
71
+ Geometry-Specific Parameters
72
+ ----------------------------
73
+ Additional parameters depend on the geometry type:
74
+ - Plane: normal vector, point on plane
75
+ - Sphere: center, radius
76
+ - Wave: amplitude, wavenumber, frequency, time
77
+
78
+ Notes
79
+ -----
80
+ Kernels are registered with the central registry using @register_kernel
81
+ decorator and IntersectionKernelID enum values.
82
+ """
83
+
84
+ def __call__(
85
+ self,
86
+ origins: DeviceNDArray,
87
+ directions: DeviceNDArray,
88
+ distances_out: DeviceNDArray,
89
+ hit_mask_out: DeviceNDArray,
90
+ normals_out: DeviceNDArray,
91
+ *args,
92
+ **kwargs,
93
+ ) -> None:
94
+ """Execute the intersection kernel."""
95
+ ...