From f022130bfad33f0f4b98bc89cde3d29e6fbd6a52 Mon Sep 17 00:00:00 2001 From: samfreund Date: Wed, 26 Nov 2025 21:10:02 -0600 Subject: [PATCH] do stuff --- photon-lib/py/docs/_stubs/wpimath/__init__.py | 5 + .../py/docs/_stubs/wpimath/_init__wpimath.py | 4 + photon-lib/py/docs/_stubs/wpimath/geometry.py | 130 ++++++++++++++++++ .../py/docs/_stubs/wpimath/interpolation.py | 12 ++ .../_stubs/wpimath/interpolation/__init__.py | 3 + .../wpimath/interpolation/_interpolation.py | 25 ++++ photon-lib/py/docs/_stubs/wpimath/units.py | 31 +++++ photon-lib/py/docs/source/conf.py | 12 +- photon-lib/py/docs/source/index.rst | 4 +- photon-lib/py/photonlibpy/packet.py | 68 ++------- .../py/photonlibpy/photonPoseEstimator.py | 16 +-- .../simulation/simCameraProperties.py | 13 +- 12 files changed, 249 insertions(+), 74 deletions(-) create mode 100644 photon-lib/py/docs/_stubs/wpimath/__init__.py create mode 100644 photon-lib/py/docs/_stubs/wpimath/_init__wpimath.py create mode 100644 photon-lib/py/docs/_stubs/wpimath/geometry.py create mode 100644 photon-lib/py/docs/_stubs/wpimath/interpolation.py create mode 100644 photon-lib/py/docs/_stubs/wpimath/interpolation/__init__.py create mode 100644 photon-lib/py/docs/_stubs/wpimath/interpolation/_interpolation.py create mode 100644 photon-lib/py/docs/_stubs/wpimath/units.py diff --git a/photon-lib/py/docs/_stubs/wpimath/__init__.py b/photon-lib/py/docs/_stubs/wpimath/__init__.py new file mode 100644 index 000000000..5488b75d6 --- /dev/null +++ b/photon-lib/py/docs/_stubs/wpimath/__init__.py @@ -0,0 +1,5 @@ +# Minimal wpimath stub for Sphinx docs build +from . import geometry +from . import units + +__all__ = ["geometry", "units"] diff --git a/photon-lib/py/docs/_stubs/wpimath/_init__wpimath.py b/photon-lib/py/docs/_stubs/wpimath/_init__wpimath.py new file mode 100644 index 000000000..658e81cb4 --- /dev/null +++ b/photon-lib/py/docs/_stubs/wpimath/_init__wpimath.py @@ -0,0 +1,4 @@ +# Stub module to match wpimath compiled module names +# This file exists so imports like `wpimath._init__wpimath` succeed during docs build. + +# no-op diff --git a/photon-lib/py/docs/_stubs/wpimath/geometry.py b/photon-lib/py/docs/_stubs/wpimath/geometry.py new file mode 100644 index 000000000..c3d2dbc68 --- /dev/null +++ b/photon-lib/py/docs/_stubs/wpimath/geometry.py @@ -0,0 +1,130 @@ +# Minimal geometry stubs for Sphinx documentation + +class Rotation3d: + def __init__(self, roll=0.0, pitch=0.0, yaw=0.0): + # store yaw as the primary rotation for simple stubs + self.roll = roll + self.pitch = pitch + self.yaw = yaw + + def toRotation2d(self): + # convert yaw to a Rotation2d for simple compatibility in docs build + return Rotation2d(self.yaw) + +class Translation3d: + def __init__(self, x=0.0, y=0.0, z=0.0): + # Support both (x, y, z) and (distance, Rotation3d) forms used by the real wpimath + # If y is a Rotation3d, compute a point at 'distance' along its yaw/pitch + try: + from math import cos, sin + except Exception: + def cos(x): + return x + def sin(x): + return x + + if hasattr(y, "yaw") and hasattr(y, "pitch"): + # interpret constructor as Translation3d(distance, Rotation3d) + distance = float(x) + pitch = float(getattr(y, "pitch", 0.0)) + yaw = float(getattr(y, "yaw", 0.0)) + # approximate spherical -> cartesian + self._x = distance * cos(pitch) * cos(yaw) + self._y = distance * cos(pitch) * sin(yaw) + self._z = distance * sin(pitch) + else: + self._x = float(x) + self._y = float(y) + self._z = float(z) + + def X(self): + return self._x + + def Y(self): + return self._y + + def Z(self): + return self._z + +class Pose3d: + def __init__(self, *args, **kwargs): + pass + +class Rotation2d: + def __init__(self, *args): + # Accept several initialization forms used in the real wpimath Rotation2d + # - Rotation2d(angle) + # - Rotation2d(fx, xOffset) used by SimCameraProperties.getPixelYaw + if len(args) == 0: + self._angle = 0.0 + elif len(args) == 1: + self._angle = float(args[0]) + else: + # fallback: when called with fx, xOffset, approximate angle as 0.0 + self._angle = 0.0 + + def degrees(self): + from math import degrees + + return degrees(self._angle) + + def radians(self): + return float(self._angle) + + def __add__(self, other): + # allow Rotation2d + Rotation2d or Rotation2d + numeric + if hasattr(other, "_angle"): + return Rotation2d(self._angle + float(other._angle)) + try: + return Rotation2d(self._angle + float(other)) + except Exception: + return NotImplemented + + def __radd__(self, other): + # numeric + Rotation2d + return self.__add__(other) + + def __sub__(self, other): + if hasattr(other, "_angle"): + return Rotation2d(self._angle - float(other._angle)) + try: + return Rotation2d(self._angle - float(other)) + except Exception: + return NotImplemented + + def __neg__(self): + return Rotation2d(-self._angle) + + def __repr__(self): + return f"Rotation2d({self._angle})" + +class Translation2d: + def __init__(self, x=0.0, y=0.0): + self._x = float(x) + self._y = float(y) + + def X(self): + return self._x + + def Y(self): + return self._y + +class Pose2d: + def __init__(self, *args, **kwargs): + pass + + def __repr__(self) -> str: + return "Pose2d()" + + +class Transform3d: + def __init__(self, *args, **kwargs): + pass + + +class Quaternion: + def __init__(self, *args, **kwargs): + pass + +# Expose names commonly used by photonlibpy +__all__ = ["Rotation3d", "Translation3d", "Pose3d", "Rotation2d", "Translation2d", "Pose2d"] diff --git a/photon-lib/py/docs/_stubs/wpimath/interpolation.py b/photon-lib/py/docs/_stubs/wpimath/interpolation.py new file mode 100644 index 000000000..4d77c9b78 --- /dev/null +++ b/photon-lib/py/docs/_stubs/wpimath/interpolation.py @@ -0,0 +1,12 @@ +# Minimal interpolation stub for docs +class TimeInterpolatableRotation2dBuffer: + def __init__(self, *args, **kwargs): + pass + + def addSample(self, *args, **kwargs): + pass + + def sample(self, *args, **kwargs): + return None + +__all__ = ["TimeInterpolatableRotation2dBuffer"] diff --git a/photon-lib/py/docs/_stubs/wpimath/interpolation/__init__.py b/photon-lib/py/docs/_stubs/wpimath/interpolation/__init__.py new file mode 100644 index 000000000..7265c77ec --- /dev/null +++ b/photon-lib/py/docs/_stubs/wpimath/interpolation/__init__.py @@ -0,0 +1,3 @@ +from ._interpolation import * + +__all__ = ["TimeInterpolatableRotation2dBuffer"] diff --git a/photon-lib/py/docs/_stubs/wpimath/interpolation/_interpolation.py b/photon-lib/py/docs/_stubs/wpimath/interpolation/_interpolation.py new file mode 100644 index 000000000..ec5fd66f5 --- /dev/null +++ b/photon-lib/py/docs/_stubs/wpimath/interpolation/_interpolation.py @@ -0,0 +1,25 @@ +# Minimal interpolation submodule stub for docs +class TimeInterpolatableRotation2dBuffer: + def __init__(self, *args, **kwargs): + pass + + def addSample(self, *args, **kwargs): + pass + + def sample(self, *args, **kwargs): + return None + + +class TimeInterpolatablePose3dBuffer: + def __init__(self, *args, **kwargs): + # buffer of Pose3d-like objects for docs import + pass + + def addSample(self, *args, **kwargs): + pass + + def sample(self, *args, **kwargs): + return None + + +__all__ = ["TimeInterpolatableRotation2dBuffer", "TimeInterpolatablePose3dBuffer"] diff --git a/photon-lib/py/docs/_stubs/wpimath/units.py b/photon-lib/py/docs/_stubs/wpimath/units.py new file mode 100644 index 000000000..577366bb2 --- /dev/null +++ b/photon-lib/py/docs/_stubs/wpimath/units.py @@ -0,0 +1,31 @@ +"""Minimal wpimath.units stub for documentation builds.""" + +def degreesToRadians(deg: float) -> float: + from math import pi + + return deg * (pi / 180.0) + + +# Represent seconds as a float alias for annotations +seconds = float + +__all__ = ["degreesToRadians", "seconds"] + +# Common unit aliases used in type annotations in WPILib stubs +meters = float +meters_per_second = float +meters_per_second_squared = float +kilograms = float +kilogram_square_meters = float + +__all__.extend([ + "meters", + "meters_per_second", + "meters_per_second_squared", + "kilograms", + "kilogram_square_meters", + "hertz", +]) + +# frequency +hertz = float diff --git a/photon-lib/py/docs/source/conf.py b/photon-lib/py/docs/source/conf.py index a21c7b28c..bbc28e37f 100644 --- a/photon-lib/py/docs/source/conf.py +++ b/photon-lib/py/docs/source/conf.py @@ -28,10 +28,20 @@ extensions = [ import os import sys +sys.path.insert( + 0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "_stubs")) +) # add docs stubs first so they shadow unavailable third-party packages + sys.path.insert( 0, os.path.abspath("../../photonlibpy") ) # adjust based on your project layout -print(sys.path) +# Mock imports that aren't available in the docs build environment so autodoc +# can import the local modules even if optional runtime deps (like wpimath) +# aren't installed. Add other names here if you see warnings for missing +# third-party packages during the build. +autodoc_mock_imports = [ + "wpilib", +] templates_path = ["_templates"] exclude_patterns = [] diff --git a/photon-lib/py/docs/source/index.rst b/photon-lib/py/docs/source/index.rst index c388e4988..fd2170002 100644 --- a/photon-lib/py/docs/source/index.rst +++ b/photon-lib/py/docs/source/index.rst @@ -4,7 +4,7 @@ contain the root `toctree` directive. PhotonLib Python Documentation -========================== +=============================== The main documentation for PhotonVision can be found at `photonvision.org `_. @@ -14,4 +14,4 @@ The main documentation for PhotonVision can be found at `photonvision.org bytes: - """ - * Returns the packet data. - * - * @return The packet data. - """ + """Return the packet data.""" return self.packetData def setData(self, data: bytes): - """ - * Sets the packet data. - * - * @param data The packet data. - """ + """Set the packet data.""" self.clear() self.packetData = data self.size = len(self.packetData) @@ -101,74 +93,42 @@ class Packet: return value def decode8(self) -> int: - """ - * Returns a single decoded byte from the packet. - * - * @return A decoded byte from the packet. - """ + """Return a single decoded byte from the packet.""" return self._decodeGeneric(" int: - """ - * Returns a single decoded short from the packet. - * - * @return A decoded short from the packet. - """ + """Return a single decoded short from the packet.""" return self._decodeGeneric(" int: - """ - * Returns a decoded int (32 bytes) from the packet. - * - * @return A decoded int from the packet. - """ + """Return a decoded 32-bit integer from the packet.""" return self._decodeGeneric(" float: - """ - * Returns a decoded float from the packet. - * - * @return A decoded float from the packet. - """ + """Return a decoded float from the packet.""" return self._decodeGeneric(" int: - """ - * Returns a decoded int64 from the packet. - * - * @return A decoded int64 from the packet. - """ + """Return a decoded 64-bit integer from the packet.""" return self._decodeGeneric(" float: - """ - * Returns a decoded double from the packet. - * - * @return A decoded double from the packet. - """ + """Return a decoded double from the packet.""" return self._decodeGeneric(" bool: - """ - * Returns a decoded boolean from the packet. - * - * @return A decoded boolean from the packet. - """ + """Return a decoded boolean from the packet.""" return self.decode8() == 1 def decodeDoubleArray(self, length: int) -> list[float]: - """ - * Returns a decoded array of floats from the packet. - """ + """Return a decoded list of doubles of the given length from the packet.""" ret = [] for _ in range(length): ret.append(self.decodeDouble()) return ret def decodeShortList(self) -> list[int]: - """ - * Returns a decoded array of shorts from the packet. - """ + """Return a decoded list of shorts from the packet (length-prefixed).""" length = self.decode8() ret = [] for _ in range(length): @@ -176,11 +136,7 @@ class Packet: return ret def decodeTransform(self) -> Transform3d: - """ - * Returns a decoded Transform3d - * - * @return A decoded Tansform3d from the packet. - """ + """Return a decoded Transform3d from the packet.""" x = self.decodeDouble() y = self.decodeDouble() z = self.decodeDouble() diff --git a/photon-lib/py/photonlibpy/photonPoseEstimator.py b/photon-lib/py/photonlibpy/photonPoseEstimator.py index 00cb0fbd2..df8fc05e2 100644 --- a/photon-lib/py/photonlibpy/photonPoseEstimator.py +++ b/photon-lib/py/photonlibpy/photonPoseEstimator.py @@ -261,18 +261,18 @@ class PhotonPoseEstimator: def update( self, cameraResult: Optional[PhotonPipelineResult] = None ) -> Optional[EstimatedRobotPose]: - """ - Updates the estimated position of the robot. Returns empty if: + """Update the estimated robot position. - - The timestamp of the provided pipeline result is the same as in the previous call to - ``update()``. + Returns empty if one of the following is true: - - No targets were found in the pipeline results. + - The timestamp of the provided pipeline result is the same as in the previous call to + ``update()``. + - No targets were found in the pipeline results. - :param cameraResult: The latest pipeline result from the camera + :param cameraResult: The latest pipeline result from the camera. - :returns: an :class:`EstimatedRobotPose` with an estimated pose, timestamp, and targets used to - create the estimate. + :returns: An :class:`EstimatedRobotPose` with an estimated pose, timestamp, and targets used + to create the estimate, or ``None`` if no estimate could be made. """ if not cameraResult: if not self._camera: diff --git a/photon-lib/py/photonlibpy/simulation/simCameraProperties.py b/photon-lib/py/photonlibpy/simulation/simCameraProperties.py index e33a1ce7a..3ce2906a8 100644 --- a/photon-lib/py/photonlibpy/simulation/simCameraProperties.py +++ b/photon-lib/py/photonlibpy/simulation/simCameraProperties.py @@ -306,14 +306,13 @@ class SimCameraProperties: :param a: The initial translation of the line :param b: The final translation of the line - :returns: A Pair of Doubles. The values may be null: + :returns: A pair of floats (t_min, t_max) where each may be ``None``: - - {Double, Double} : Two parametrized values(t), minimum first, representing which - segment of the line is visible in the camera frustum. - - {Double, null} : One value(t) representing a single intersection point. For example, - the line only intersects the intersection of two adjacent viewplanes. - - {null, null} : No values. The line segment is not visible in the camera frustum. - """ + - ``(float, float)``: Two t values (minimum first) representing the visible segment. + - ``(float, None)``: A single intersection point. + - ``(None, None)``: No intersection; the segment is not visible. + + """ # translations relative to the camera relA = camRt.applyTranslation(a)