# The Clear BSD License
#
# Copyright (c) 2026 Tobias Heibges
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted (subject to the limitations in the disclaimer
# below) provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""
DetectorProtocol - Protocol definition for detector implementations.
This module defines the DetectorProtocol that all detector classes should implement,
ensuring a consistent interface across small (point) detectors and extended
(surface) detectors.
Examples
--------
>>> from lsurf.detectors.protocol import DetectorProtocol
>>>
>>> def process_detector(detector: DetectorProtocol, rays: RayBatch):
... result = detector.detect(rays)
... print(f"Detected {result.num_rays} rays")
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Protocol, Union, runtime_checkable
if TYPE_CHECKING:
from numpy.typing import NDArray
from ..utilities.ray_data import RayBatch
from .results import DetectorResult
[docs]
@runtime_checkable
class DetectorProtocol(Protocol):
"""
Protocol defining the interface for all detector implementations.
All detectors (small/point detectors and extended/surface detectors)
should implement this protocol for consistent behavior.
Attributes
----------
name : str
Human-readable detector name for identification
Methods
-------
detect(rays) -> DetectorResult
Detect rays and return results
clear()
Clear accumulated detection data
Examples
--------
>>> class MyDetector:
... def __init__(self, name: str = "My Detector"):
... self.name = name
... self._result = DetectorResult.empty(name)
...
... def detect(self, rays: RayBatch) -> DetectorResult:
... # Detection logic here
... return self._result
...
... def clear(self) -> None:
... self._result = DetectorResult.empty(self.name)
"""
name: str
[docs]
def detect(self, rays: "RayBatch") -> "DetectorResult":
"""
Detect rays and return detection results.
Parameters
----------
rays : RayBatch
Ray batch to test for detection
Returns
-------
DetectorResult
Detection results containing all rays that hit this detector
"""
...
[docs]
def clear(self) -> None:
"""
Clear accumulated detection data.
Resets the detector to its initial state with no recorded detections.
"""
...
[docs]
@runtime_checkable
class AccumulatingDetectorProtocol(DetectorProtocol, Protocol):
"""
Protocol for detectors that accumulate results over multiple detect() calls.
These detectors maintain internal state and can return cumulative results.
Attributes
----------
name : str
Human-readable detector name
accumulated_result : DetectorResult
All accumulated detections since last clear()
Examples
--------
>>> detector = SphericalDetector(center=(0, 0, 100), radius=10)
>>> result1 = detector.detect(rays1)
>>> result2 = detector.detect(rays2)
>>> total = detector.accumulated_result # Contains both result1 and result2
>>> detector.clear() # Reset accumulation
"""
accumulated_result: "DetectorResult"
[docs]
@runtime_checkable
class ExtendedDetectorProtocol(DetectorProtocol, Protocol):
"""
Protocol for extended (surface) detectors with additional geometric properties.
Extended detectors have a defined geometric surface and can provide
additional information about the detection geometry.
Attributes
----------
name : str
Human-readable detector name
sphere_radius : float
Radius of the detection sphere (for spherical detectors)
center : ndarray, shape (3,)
Center position of the detector
Examples
--------
>>> from lsurf.detectors import RecordingSphereDetector
>>> detector = RecordingSphereDetector(altitude=33000.0)
>>> print(detector.sphere_radius) # Earth radius + altitude
"""
@property
def sphere_radius(self) -> float:
"""Radius of the detection sphere in meters."""
...
@property
def center(self) -> "NDArray":
"""Center position of the detector."""
...
# Type alias for any detector
AnyDetector = Union[
DetectorProtocol, AccumulatingDetectorProtocol, ExtendedDetectorProtocol
]