From a64491a59e860c18014721e5ae8a4cfd7d8a11a1 Mon Sep 17 00:00:00 2001 From: Lucien Morey Date: Thu, 14 Nov 2024 02:39:02 +1100 Subject: [PATCH] [photonlibpy] add mypy to ci (#1570) Co-authored-by: James Ward --- .github/workflows/python.yml | 9 ++++- .../py/photonlibpy/estimation/openCVHelp.py | 1 - .../py/photonlibpy/estimation/targetModel.py | 4 ++- .../estimation/visionEstimation.py | 8 ++--- .../generated/MultiTargetPNPResultSerde.py | 8 ++++- .../generated/PhotonPipelineMetadataSerde.py | 7 +++- .../generated/PhotonPipelineResultSerde.py | 10 +++++- .../generated/PhotonTrackedTargetSerde.py | 8 ++++- .../photonlibpy/generated/PnpResultSerde.py | 7 +++- .../generated/TargetCornerSerde.py | 7 +++- .../photonlibpy/simulation/photonCameraSim.py | 14 ++++---- .../simulation/simCameraProperties.py | 2 ++ .../photonlibpy/simulation/visionSystemSim.py | 4 +-- .../py/photonlibpy/targeting/TargetCorner.py | 4 +-- .../targeting/multiTargetPNPResult.py | 7 ++-- .../targeting/photonPipelineResult.py | 7 ++-- .../targeting/photonTrackedTarget.py | 4 +-- photon-lib/py/pyproject.toml | 2 ++ photon-lib/py/test/visionSystemSim_test.py | 34 +++++++++---------- photon-serde/templates/ThingSerde.py.jinja | 18 ++++++++-- .../struct/PhotonPipelineResultSerde.java | 2 +- 21 files changed, 116 insertions(+), 51 deletions(-) create mode 100644 photon-lib/py/pyproject.toml diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index cfe5af2c3..24b1def22 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -37,7 +37,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools wheel pytest + pip install setuptools wheel pytest mypy - name: Build wheel working-directory: ./photon-lib/py @@ -50,6 +50,13 @@ jobs: pip install --no-cache-dir dist/*.whl pytest + - name: Run mypy type checking + uses: liskin/gh-problem-matcher-wrap@v3 + with: + linters: mypy + run: | + mypy --show-column-numbers --config-file photon-lib/py/pyproject.toml photon-lib + - name: Upload artifacts uses: actions/upload-artifact@master diff --git a/photon-lib/py/photonlibpy/estimation/openCVHelp.py b/photon-lib/py/photonlibpy/estimation/openCVHelp.py index 8a6f4911f..eaf310624 100644 --- a/photon-lib/py/photonlibpy/estimation/openCVHelp.py +++ b/photon-lib/py/photonlibpy/estimation/openCVHelp.py @@ -128,7 +128,6 @@ class OpenCVHelp: alt: Transform3d | None = None reprojectionError: cv.typing.MatLike | None = None best: Transform3d = Transform3d() - alt: Transform3d | None = None for tries in range(2): retval, rvecs, tvecs, reprojectionError = cv.solvePnPGeneric( diff --git a/photon-lib/py/photonlibpy/estimation/targetModel.py b/photon-lib/py/photonlibpy/estimation/targetModel.py index e9006b50b..652863403 100644 --- a/photon-lib/py/photonlibpy/estimation/targetModel.py +++ b/photon-lib/py/photonlibpy/estimation/targetModel.py @@ -18,6 +18,8 @@ class TargetModel: verts: List[Translation3d] | None = None ): + self.vertices: List[Translation3d] = [] + if ( width is not None and height is not None @@ -88,7 +90,7 @@ class TargetModel: self.isSpherical = False if len(verts) <= 2: - self.vertices: List[Translation3d] = [] + self.vertices = [] self.isPlanar = False else: cornersPlaner = True diff --git a/photon-lib/py/photonlibpy/estimation/visionEstimation.py b/photon-lib/py/photonlibpy/estimation/visionEstimation.py index 834df03d6..eaa4b0d33 100644 --- a/photon-lib/py/photonlibpy/estimation/visionEstimation.py +++ b/photon-lib/py/photonlibpy/estimation/visionEstimation.py @@ -16,10 +16,10 @@ class VisionEstimation: id = tag.getFiducialId() maybePose = layout.getTagPose(id) if maybePose: - tag = AprilTag() - tag.ID = id - tag.pose = maybePose - retVal.append(tag) + aprilTag = AprilTag() + aprilTag.ID = id + aprilTag.pose = maybePose + retVal.append(aprilTag) return retVal @staticmethod diff --git a/photon-lib/py/photonlibpy/generated/MultiTargetPNPResultSerde.py b/photon-lib/py/photonlibpy/generated/MultiTargetPNPResultSerde.py index 84bea5502..53e381351 100644 --- a/photon-lib/py/photonlibpy/generated/MultiTargetPNPResultSerde.py +++ b/photon-lib/py/photonlibpy/generated/MultiTargetPNPResultSerde.py @@ -20,8 +20,14 @@ ## --> DO NOT MODIFY <-- ############################################################################### +from typing import TYPE_CHECKING + from ..packet import Packet -from ..targeting import * +from ..targeting import * # noqa + +if TYPE_CHECKING: + from ..targeting import MultiTargetPNPResult # noqa + from ..targeting import PnpResult # noqa class MultiTargetPNPResultSerde: diff --git a/photon-lib/py/photonlibpy/generated/PhotonPipelineMetadataSerde.py b/photon-lib/py/photonlibpy/generated/PhotonPipelineMetadataSerde.py index be9ed5f2f..af4914aaa 100644 --- a/photon-lib/py/photonlibpy/generated/PhotonPipelineMetadataSerde.py +++ b/photon-lib/py/photonlibpy/generated/PhotonPipelineMetadataSerde.py @@ -20,8 +20,13 @@ ## --> DO NOT MODIFY <-- ############################################################################### +from typing import TYPE_CHECKING + from ..packet import Packet -from ..targeting import * +from ..targeting import * # noqa + +if TYPE_CHECKING: + from ..targeting import PhotonPipelineMetadata # noqa class PhotonPipelineMetadataSerde: diff --git a/photon-lib/py/photonlibpy/generated/PhotonPipelineResultSerde.py b/photon-lib/py/photonlibpy/generated/PhotonPipelineResultSerde.py index 45abf0b4d..5023bfe2a 100644 --- a/photon-lib/py/photonlibpy/generated/PhotonPipelineResultSerde.py +++ b/photon-lib/py/photonlibpy/generated/PhotonPipelineResultSerde.py @@ -20,8 +20,16 @@ ## --> DO NOT MODIFY <-- ############################################################################### +from typing import TYPE_CHECKING + from ..packet import Packet -from ..targeting import * +from ..targeting import * # noqa + +if TYPE_CHECKING: + from ..targeting import MultiTargetPNPResult # noqa + from ..targeting import PhotonPipelineMetadata # noqa + from ..targeting import PhotonPipelineResult # noqa + from ..targeting import PhotonTrackedTarget # noqa class PhotonPipelineResultSerde: diff --git a/photon-lib/py/photonlibpy/generated/PhotonTrackedTargetSerde.py b/photon-lib/py/photonlibpy/generated/PhotonTrackedTargetSerde.py index 655147f08..f39f73a3b 100644 --- a/photon-lib/py/photonlibpy/generated/PhotonTrackedTargetSerde.py +++ b/photon-lib/py/photonlibpy/generated/PhotonTrackedTargetSerde.py @@ -20,8 +20,14 @@ ## --> DO NOT MODIFY <-- ############################################################################### +from typing import TYPE_CHECKING + from ..packet import Packet -from ..targeting import * +from ..targeting import * # noqa + +if TYPE_CHECKING: + from ..targeting import PhotonTrackedTarget # noqa + from ..targeting import TargetCorner # noqa class PhotonTrackedTargetSerde: diff --git a/photon-lib/py/photonlibpy/generated/PnpResultSerde.py b/photon-lib/py/photonlibpy/generated/PnpResultSerde.py index 631257422..ac83f1691 100644 --- a/photon-lib/py/photonlibpy/generated/PnpResultSerde.py +++ b/photon-lib/py/photonlibpy/generated/PnpResultSerde.py @@ -20,8 +20,13 @@ ## --> DO NOT MODIFY <-- ############################################################################### +from typing import TYPE_CHECKING + from ..packet import Packet -from ..targeting import * +from ..targeting import * # noqa + +if TYPE_CHECKING: + from ..targeting import PnpResult # noqa class PnpResultSerde: diff --git a/photon-lib/py/photonlibpy/generated/TargetCornerSerde.py b/photon-lib/py/photonlibpy/generated/TargetCornerSerde.py index 6c60e0a23..250ababf0 100644 --- a/photon-lib/py/photonlibpy/generated/TargetCornerSerde.py +++ b/photon-lib/py/photonlibpy/generated/TargetCornerSerde.py @@ -20,8 +20,13 @@ ## --> DO NOT MODIFY <-- ############################################################################### +from typing import TYPE_CHECKING + from ..packet import Packet -from ..targeting import * +from ..targeting import * # noqa + +if TYPE_CHECKING: + from ..targeting import TargetCorner # noqa class TargetCornerSerde: diff --git a/photon-lib/py/photonlibpy/simulation/photonCameraSim.py b/photon-lib/py/photonlibpy/simulation/photonCameraSim.py index bdff61012..ae65ed9eb 100644 --- a/photon-lib/py/photonlibpy/simulation/photonCameraSim.py +++ b/photon-lib/py/photonlibpy/simulation/photonCameraSim.py @@ -41,7 +41,9 @@ class PhotonCameraSim: self.videoSimRawEnabled: bool = False self.videoSimWireframeEnabled: bool = False self.videoSimWireframeResolution: float = 0.1 - self.videoSimProcEnabled: bool = True + self.videoSimProcEnabled: bool = ( + False # TODO switch this back to default True when the functionality is enabled + ) self.heartbeatCounter: int = 0 self.nextNtEntryTime = int(wpilib.Timer.getFPGATimestamp() * 1e6) self.tagLayout = AprilTagFieldLayout.loadField(AprilTagField.k2024Crescendo) @@ -191,19 +193,19 @@ class PhotonCameraSim: self.maxSightRange = range def enableRawStream(self, enabled: bool) -> None: + self.videoSimRawEnabled = enabled raise Exception("Raw stream not implemented") - # self.videoSimRawEnabled = enabled def enableDrawWireframe(self, enabled: bool) -> None: + self.videoSimWireframeEnabled = enabled raise Exception("Wireframe not implemented") - # self.videoSimWireframeEnabled = enabled def setWireframeResolution(self, resolution: float) -> None: self.videoSimWireframeResolution = resolution def enableProcessedStream(self, enabled: bool) -> None: + self.videoSimProcEnabled = enabled raise Exception("Processed stream not implemented") - # self.videoSimProcEnabled = enabled def process( self, latency: seconds, cameraPose: Pose3d, targets: list[VisionTargetSim] @@ -321,13 +323,13 @@ class PhotonCameraSim: ) # Video streams disabled for now - if self.enableRawStream: + if self.videoSimRawEnabled: # VideoSimUtil::UpdateVideoProp(videoSimRaw, prop); # cv::Size videoFrameSize{prop.GetResWidth(), prop.GetResHeight()}; # cv::Mat blankFrame = cv::Mat::zeros(videoFrameSize, CV_8UC1); # blankFrame.assignTo(videoSimFrameRaw); pass - if self.enableProcessedStream: + if self.videoSimProcEnabled: # VideoSimUtil::UpdateVideoProp(videoSimProcessed, prop); pass diff --git a/photon-lib/py/photonlibpy/simulation/simCameraProperties.py b/photon-lib/py/photonlibpy/simulation/simCameraProperties.py index 3ae234b52..6869e594f 100644 --- a/photon-lib/py/photonlibpy/simulation/simCameraProperties.py +++ b/photon-lib/py/photonlibpy/simulation/simCameraProperties.py @@ -49,6 +49,8 @@ class SimCameraProperties: raise Exception("not a correct function sig") if has_fov_args: + # really convince python we are doing the right thing + assert fovDiag is not None if fovDiag.degrees() < 1.0 or fovDiag.degrees() > 179.0: fovDiag = Rotation2d.fromDegrees( max(min(fovDiag.degrees(), 179.0), 1.0) diff --git a/photon-lib/py/photonlibpy/simulation/visionSystemSim.py b/photon-lib/py/photonlibpy/simulation/visionSystemSim.py index 0c5903d62..ca42004c0 100644 --- a/photon-lib/py/photonlibpy/simulation/visionSystemSim.py +++ b/photon-lib/py/photonlibpy/simulation/visionSystemSim.py @@ -228,8 +228,8 @@ class VisionSystemSim: camResult = camSim.process(latency, lateCameraPose, allTargets) camSim.submitProcessedFrame(camResult, timestampNt) - for target in camResult.getTargets(): - trf = target.getBestCameraToTarget() + for tgt in camResult.getTargets(): + trf = tgt.getBestCameraToTarget() if trf == Transform3d(): continue diff --git a/photon-lib/py/photonlibpy/targeting/TargetCorner.py b/photon-lib/py/photonlibpy/targeting/TargetCorner.py index 849a586eb..76e113eb4 100644 --- a/photon-lib/py/photonlibpy/targeting/TargetCorner.py +++ b/photon-lib/py/photonlibpy/targeting/TargetCorner.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, ClassVar if TYPE_CHECKING: - from .. import generated + from ..generated.TargetCornerSerde import TargetCornerSerde @dataclass @@ -10,4 +10,4 @@ class TargetCorner: x: float = 0 y: float = 9 - photonStruct: ClassVar["generated.TargetCornerSerde"] + photonStruct: ClassVar["TargetCornerSerde"] diff --git a/photon-lib/py/photonlibpy/targeting/multiTargetPNPResult.py b/photon-lib/py/photonlibpy/targeting/multiTargetPNPResult.py index 1eb03ea60..985158db5 100644 --- a/photon-lib/py/photonlibpy/targeting/multiTargetPNPResult.py +++ b/photon-lib/py/photonlibpy/targeting/multiTargetPNPResult.py @@ -4,7 +4,8 @@ from typing import TYPE_CHECKING, ClassVar from wpimath.geometry import Transform3d if TYPE_CHECKING: - from .. import generated + from ..generated.MultiTargetPNPResultSerde import MultiTargetPNPResultSerde + from ..generated.PnpResultSerde import PnpResultSerde @dataclass @@ -15,7 +16,7 @@ class PnpResult: bestReprojErr: float = 0.0 altReprojErr: float = 0.0 - photonStruct: ClassVar["generated.PnpResultSerde"] + photonStruct: ClassVar["PnpResultSerde"] @dataclass @@ -25,4 +26,4 @@ class MultiTargetPNPResult: estimatedPose: PnpResult = field(default_factory=PnpResult) fiducialIDsUsed: list[int] = field(default_factory=list) - photonStruct: ClassVar["generated.MultiTargetPNPResultSerde"] + photonStruct: ClassVar["MultiTargetPNPResultSerde"] diff --git a/photon-lib/py/photonlibpy/targeting/photonPipelineResult.py b/photon-lib/py/photonlibpy/targeting/photonPipelineResult.py index 67e722456..dc1ff2436 100644 --- a/photon-lib/py/photonlibpy/targeting/photonPipelineResult.py +++ b/photon-lib/py/photonlibpy/targeting/photonPipelineResult.py @@ -5,7 +5,8 @@ from .multiTargetPNPResult import MultiTargetPNPResult from .photonTrackedTarget import PhotonTrackedTarget if TYPE_CHECKING: - from .. import generated + from ..generated.PhotonPipelineMetadataSerde import PhotonPipelineMetadataSerde + from ..generated.PhotonPipelineResultSerde import PhotonPipelineResultSerde @dataclass @@ -20,7 +21,7 @@ class PhotonPipelineMetadata: timeSinceLastPong: int = -1 - photonStruct: ClassVar["generated.PhotonPipelineMetadataSerde"] + photonStruct: ClassVar["PhotonPipelineMetadataSerde"] @dataclass @@ -69,4 +70,4 @@ class PhotonPipelineResult: return None return self.getTargets()[0] - photonStruct: ClassVar["generated.PhotonPipelineResultSerde"] + photonStruct: ClassVar["PhotonPipelineResultSerde"] diff --git a/photon-lib/py/photonlibpy/targeting/photonTrackedTarget.py b/photon-lib/py/photonlibpy/targeting/photonTrackedTarget.py index 4ad3668a5..e3e253bb9 100644 --- a/photon-lib/py/photonlibpy/targeting/photonTrackedTarget.py +++ b/photon-lib/py/photonlibpy/targeting/photonTrackedTarget.py @@ -7,7 +7,7 @@ from ..packet import Packet from .TargetCorner import TargetCorner if TYPE_CHECKING: - from .. import generated + from ..generated.PhotonTrackedTargetSerde import PhotonTrackedTargetSerde @dataclass @@ -63,4 +63,4 @@ class PhotonTrackedTarget: retList.append(TargetCorner(cx, cy)) return retList - photonStruct: ClassVar["generated.PhotonTrackedTargetSerde"] + photonStruct: ClassVar["PhotonTrackedTargetSerde"] diff --git a/photon-lib/py/pyproject.toml b/photon-lib/py/pyproject.toml new file mode 100644 index 000000000..4dd0d5078 --- /dev/null +++ b/photon-lib/py/pyproject.toml @@ -0,0 +1,2 @@ +[tool.mypy] +exclude = ["build","setup.py"] diff --git a/photon-lib/py/test/visionSystemSim_test.py b/photon-lib/py/test/visionSystemSim_test.py index 4851f8190..c9f4757e2 100644 --- a/photon-lib/py/test/visionSystemSim_test.py +++ b/photon-lib/py/test/visionSystemSim_test.py @@ -25,7 +25,7 @@ def setupCommon() -> None: setVersionCheckEnabled(False) -def test_VisibilityCupidShuffle(): +def test_VisibilityCupidShuffle() -> None: targetPose = Pose3d(Translation3d(15.98, 0.0, 2.0), Rotation3d(0, 0, math.pi)) @@ -83,7 +83,7 @@ def test_VisibilityCupidShuffle(): assert camera.getLatestResult().hasTargets() -def test_NotVisibleVert1(): +def test_NotVisibleVert1() -> None: targetPose = Pose3d(Translation3d(15.98, 0.0, 2.0), Rotation3d(0, 0, math.pi)) @@ -111,7 +111,7 @@ def test_NotVisibleVert1(): assert not camera.getLatestResult().hasTargets() -def test_NotVisibleVert2(): +def test_NotVisibleVert2() -> None: targetPose = Pose3d(Translation3d(15.98, 0.0, 2.0), Rotation3d(0, 0, math.pi)) @@ -138,7 +138,7 @@ def test_NotVisibleVert2(): assert not camera.getLatestResult().hasTargets() -def test_NotVisibleTargetSize(): +def test_NotVisibleTargetSize() -> None: targetPose = Pose3d(Translation3d(15.98, 0.0, 1.0), Rotation3d(0, 0, math.pi)) visionSysSim = VisionSystemSim("Test") @@ -161,7 +161,7 @@ def test_NotVisibleTargetSize(): assert not camera.getLatestResult().hasTargets() -def test_NotVisibleTooFarLeds(): +def test_NotVisibleTooFarLeds() -> None: targetPose = Pose3d(Translation3d(15.98, 0.0, 1.0), Rotation3d(0, 0, math.pi)) @@ -189,7 +189,7 @@ def test_NotVisibleTooFarLeds(): @pytest.mark.parametrize( "expected_yaw", [-10.0, -5.0, -2.0, -1.0, 0.0, 5.0, 7.0, 10.23] ) -def test_YawAngles(expected_yaw): +def test_YawAngles(expected_yaw) -> None: targetPose = Pose3d( Translation3d(15.98, 0.0, 1.0), Rotation3d(0.0, 0.0, 3.0 * math.pi / 4.0) @@ -211,14 +211,15 @@ def test_YawAngles(expected_yaw): result = camera.getLatestResult() - assert result.hasTargets() - assert result.getBestTarget().getYaw() == pytest.approx(expected_yaw, abs=0.25) + bestTarget = result.getBestTarget() + assert bestTarget is not None + assert bestTarget.getYaw() == pytest.approx(expected_yaw, abs=0.25) @pytest.mark.parametrize( "expected_pitch", [-10.0, -5.0, -2.0, -1.0, 0.0, 5.0, 7.0, 10.23] ) -def test_PitchAngles(expected_pitch): +def test_PitchAngles(expected_pitch) -> None: targetPose = Pose3d( Translation3d(15.98, 0.0, 0.0), Rotation3d(0, 0, 3.0 * math.pi / 4.0) @@ -245,8 +246,9 @@ def test_PitchAngles(expected_pitch): result = camera.getLatestResult() - assert result.hasTargets() - assert result.getBestTarget().getPitch() == pytest.approx(expected_pitch, abs=0.25) + bestTarget = result.getBestTarget() + assert bestTarget is not None + assert bestTarget.getPitch() == pytest.approx(expected_pitch, abs=0.25) @pytest.mark.parametrize( @@ -271,7 +273,7 @@ def test_PitchAngles(expected_pitch): (19.52, -15.98, 1.1), ], ) -def test_distanceCalc(distParam, pitchParam, heightParam): +def test_distanceCalc(distParam, pitchParam, heightParam) -> None: distParam = feetToMeters(distParam) pitchParam = math.radians(pitchParam) heightParam = feetToMeters(heightParam) @@ -301,10 +303,8 @@ def test_distanceCalc(distParam, pitchParam, heightParam): result = camera.getLatestResult() - assert result.hasTargets() - target = result.getBestTarget() - + assert target is not None assert target.getYaw() == pytest.approx(0.0, abs=0.5) # TODO Enable when PhotonUtils is ported @@ -314,7 +314,7 @@ def test_distanceCalc(distParam, pitchParam, heightParam): # assert dist == pytest.approx(distParam, abs=0.25) -def test_MultipleTargets(): +def test_MultipleTargets() -> None: targetPoseL = Pose3d(Translation3d(15.98, 2.0, 0.0), Rotation3d(0.0, 0.0, math.pi)) targetPoseC = Pose3d(Translation3d(15.98, 0.0, 0.0), Rotation3d(0.0, 0.0, math.pi)) targetPoseR = Pose3d(Translation3d(15.98, -2.0, 0.0), Rotation3d(0.0, 0.0, math.pi)) @@ -415,7 +415,7 @@ def test_MultipleTargets(): assert len(tgtList) == 11 -def test_PoseEstimation(): +def test_PoseEstimation() -> None: visionSysSim = VisionSystemSim("Test") camera = PhotonCamera("camera") cameraSim = PhotonCameraSim(camera) diff --git a/photon-serde/templates/ThingSerde.py.jinja b/photon-serde/templates/ThingSerde.py.jinja index f6e441b47..82cedc435 100644 --- a/photon-serde/templates/ThingSerde.py.jinja +++ b/photon-serde/templates/ThingSerde.py.jinja @@ -20,11 +20,25 @@ ## --> DO NOT MODIFY <-- ############################################################################### -from ..targeting import * +from typing import TYPE_CHECKING + from ..packet import Packet +from ..targeting import * # noqa + +if TYPE_CHECKING: +{%- set ns = namespace(types=[]) -%} +{%- for field in fields|unique(attribute="type")-%} + {%- set _ = ns.types.append(field.type) -%} +{%- endfor -%} +{% set _ = ns.types.append(name) -%} +{%- for type in ns.types|sort%} +{%- if not type | is_shimmed and not type | is_intrinsic %} + from ..targeting import {{ type }} # noqa +{%- endif %} +{%- endfor%} + class {{ name }}Serde: - # Message definition md5sum. See photon_packet.adoc for details MESSAGE_VERSION = "{{ message_hash }}" MESSAGE_FORMAT = "{{ message_fmt }}" diff --git a/photon-targeting/src/generated/main/java/org/photonvision/struct/PhotonPipelineResultSerde.java b/photon-targeting/src/generated/main/java/org/photonvision/struct/PhotonPipelineResultSerde.java index d0ed4f9ab..9bac6213c 100644 --- a/photon-targeting/src/generated/main/java/org/photonvision/struct/PhotonPipelineResultSerde.java +++ b/photon-targeting/src/generated/main/java/org/photonvision/struct/PhotonPipelineResultSerde.java @@ -78,7 +78,7 @@ public class PhotonPipelineResultSerde implements PacketSerde[] getNestedPhotonMessages() { return new PacketSerde[] { - MultiTargetPNPResult.photonStruct,PhotonTrackedTarget.photonStruct,PhotonPipelineMetadata.photonStruct + MultiTargetPNPResult.photonStruct,PhotonPipelineMetadata.photonStruct,PhotonTrackedTarget.photonStruct }; }