Skip to content

Commit b15fa35

Browse files
facontidavideDavide Faconticlaude
authored
feat: add 7 new canonical builtin object types (#90)
* feat(proto): add canonical builtin object wire schemas Wire-format contracts for the byte-backed builtin object types that PlotJuggler plugins emit and renderers consume: - Image, DepthImage, PointCloud: pair with existing SDK structs - CompressedPointCloud, OccupancyGrid: new byte-backed types - SceneEntities + 8 scene primitives (Arrow, Cube, Sphere, Cylinder, Line, Triangle, Text, Axes) for marker-style 3D visualization - Mesh3D, VideoFrame: opaque-asset family (binary payload + format identifier) Schema cleanup: - Add Pose to Geometry.proto (shared by scene primitives, OccupancyGrid, Mesh3D) - Merge FrameTransform.proto into FrameTransforms.proto to match the single-file-per-family pattern - Add pj/README.md describing the purpose of each schema Tooling: - Extend .clang-format with a Proto language section (Google base, 120-col limit) and enable the existing clang-format pre-commit hook on .proto files. - Pass --allow-multiple-documents to the check-yaml hook so it accepts the multi-doc .clang-format file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(builtin): add SDK structs for new canonical object types Catalog additions matching the proto contracts landed in f37b7e4. This is the "top-level" change: BuiltinObjectType enum + SDK struct headers. Codecs follow in subsequent commits. New SDK structs: - sdk::OccupancyGrid: 2D metric grid (maps, costmaps) - sdk::CompressedPointCloud: opaque compressed cloud (Draco, ...) - sdk::Mesh3D: binary mesh asset (GLTF/STL/PLY/OBJ/USD/DAE) - sdk::VideoFrame: single frame of h264/h265/vp9/av1 stream - sdk::SceneEntities + 8 primitives (Arrow, Cube, Sphere, Cylinder, Line, Triangle, Text, Axes) for marker-style 3D visualization Also adds sdk::Pose to FrameTransforms.hpp (consumed by Mesh3D, OccupancyGrid, and the scene primitives). Long-term it belongs in a dedicated Geometry.hpp alongside Vector3/Quaternion/Point2/Point3 - separate refactor. BuiltinObject.hpp: - Enum gains 5 entries (kOccupancyGrid through kSceneEntities at numeric values 7..11; preserves the historical gap at 2). - name(), parseBuiltinObjectType(), and typeOf() extended to cover every new entry. Tests: builtin_object_test.cpp gains a round-trip test that iterates every enum value through name() / parseBuiltinObjectType() to catch future drift between the three helpers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(builtin): add codecs for 5 new canonical object types Hand-rolled protobuf codecs implementing the wire contracts landed in f37b7e4 for the SDK structs landed in 620f3da. Round-trip tests follow in a separate commit. New codecs (each with serialize / deserialize / kSchema constant): - video_frame_codec - compressed_point_cloud_codec - occupancy_grid_codec - mesh3d_codec - scene_entities_codec (also covers the 8 scene primitives: Arrow, Cube, Sphere, Cylinder, Line, Triangle, Text, Axes) Codec infrastructure: - protobuf_wire.hpp gains a parseFields(reader, handler) helper that drains the standard tag-read / switch / skip-unknown decode loop — cuts ~10 lines of boilerplate per nested message. - protobuf_wire.hpp gains fixed32 / packedFixed32 / readFixed32 / readPackedFixed32 for LinePrimitive.indices and similar. - Reader.readBytes is promoted from private to public so byte-backed codecs can copy bytes into their owned vector + BufferAnchor. - New internal geometry_codec.hpp consolidates the shared writeTimestamp / writeVector3 / writeQuaternion / writePose / writeColor / writePoint3 + decode counterparts. Pulled out of frame_transforms_codec.cpp; frame_transforms now includes it. Pure code move — wire format unchanged, existing tests pass. Existing image_annotations_codec.cpp is NOT refactored in this PR (it has its own writeColor / decodeColor that match byte-identically). That refactor is left for a follow-up to keep this PR scoped. Build wire-up: 5 new .cpp files added to pj_base library sources. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(builtin): add codecs for Image, DepthImage, PointCloud The SDK structs predate the catalog rework; their wire contracts landed in f37b7e4. This commit closes the codec gap so all byte-backed builtin types have round-trippable serialization. New codecs (each with serialize / deserialize / kSchema): - image_codec — kSchemaImage = "PJ.Image" - depth_image_codec — kSchemaDepthImage = "PJ.DepthImage" - point_cloud_codec — kSchemaPointCloud = "PJ.PointCloud" Codec infrastructure additions to protobuf_wire.hpp: - Writer.floatField + Reader.readFloat for `optional float` (used by Image.compressed_depth_min / _max). - Writer.packedDouble (range-based) for `repeated double` fields (DepthImage.K, DepthImage.D). - readPackedDouble / readPackedDoubleArray<N> free helpers for the decode side. The fixed-size-array variant guards DepthImage.K's 9-entry contract at the wire boundary. Misc: media_metadata_test.cpp uses "PJ.VideoFrame" in its example metadata payload instead of the now-renamed "PJ.CompressedVideo". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(builtin): add AssetVideo type for file-backed video assets AssetVideo carries no pixel data — it references a video file by path and surfaces decode-routing metadata (MIME type, dimensions, frame rate) so consumers can size playback windows and route to a decoder without opening the file. Producers (LeRobot dataset loaders, MP4 loaders) push exactly one AssetVideo per topic; the ObjectStore timestamp of that entry equals `time_origin_ns` so timeline UIs see the asset's start instant. Distinct from VideoFrame (which carries a single coded frame as opaque bytes) — AssetVideo is the file-reference family alongside Mesh3D, not the streamed-payload family. Adds: - pj_base/proto/pj/AssetVideo.proto - pj_base/include/pj_base/builtin/AssetVideo.hpp (SDK struct) - pj_base/include/pj_base/builtin/asset_video_codec.hpp - pj_base/src/builtin/asset_video_codec.cpp - kAssetVideo = 12 in BuiltinObjectType + name/parse/typeOf entries - Tests extended to cover round-trip through the catalog helpers SDK-side `time_origin_ns` and `duration_ns` are std::optional — absent means "not aligned to wall clock" / "probe the file" respectively. The codec emits these fields only when set, and on decode leaves them as nullopt when the field is absent on the wire. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: rename builtin SDK headers to snake_case The 12 SDK builtin headers under `pj_base/include/pj_base/builtin/` were the only CamelCase filenames in a codebase that is otherwise snake_case throughout. Rename them to match (e.g. `Image.hpp` → `image.hpp`, `SceneEntities.hpp` → `scene_entities.hpp`); update all `#include` directives, `@file` Doxygen tags, and the include snippet in `docs/builtin_type.md`. Proto files keep their CamelCase names — they mirror the contained message name (e.g. `Image.proto` → `message Image`), a separate convention scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test: add round-trip tests for all 9 new builtin codecs Each new codec gets a small gtest file covering its schema-name constant, the empty-buffer error path, and a realistic round-trip through serialize/deserialize. SceneEntities additionally exercises one entity carrying every primitive kind plus per-vertex colors and indices on a LinePrimitive; Image/DepthImage/Mesh3D cover their two mutually-exclusive shapes (compressed-depth vs raw, plumb_bob vs rectified, embedded asset vs URL). Hand-rolled protobuf builders (varint/tag/double/fixed32/length- delimited/string/bytes) plus geometry encoders (Timestamp, Vector3, Quaternion, Pose) are extracted into a shared `pj_base/tests/protobuf_wire_test_helpers.hpp`. The existing frame_transforms_codec_test.cpp and image_annotations_codec_test.cpp adopt it; their golden-byte assertions remain unchanged so they keep guarding the on-wire format. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: extend builtin_type.md with the new canonical types Document the 6 new builtin types (OccupancyGrid, CompressedPointCloud, Mesh3D, VideoFrame, AssetVideo, SceneEntities) added on this branch: update the public-headers include block, extend the serialization- families table (the byte-backed views family grew from 3 to 7; the owned-values family gained SceneEntities and AssetVideo), add the new kEnum rows to the classification table, and write a brief per-type section in the same shape as the existing Image/DepthImage/PointCloud sections. Also expand the Conversion Examples table with the new source-side inputs each type covers (Draco clouds, nav_msgs/OccupancyGrid, URDF mesh resources, marker arrays, raw codec frames, MP4 dataset files). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(pj_base): add RobotDescription builtin object type Stage B of the pj_scene3D PJ4-integration plan. Adds a canonical SDK type for URDF / SDF / MJCF robot descriptions so the upcoming 3D widget can consume them through the same ObjectStore path as other builtin objects (PointCloud, FrameTransforms, Image, etc.). - New PJ::sdk::RobotDescription owned struct: timestamp_ns, topic, format (open-ended hint string, mirrors Image::encoding), text (raw source markup). Header lives at pj_base/builtin/robot_description.hpp (snake_case to match the rest of this branch). - BuiltinObjectType::kRobotDescription = 13, appended after kAssetVideo. The original branch assigned slot 7, which collided with the kOccupancyGrid = 7 assignment landing in this PR; renumbered up to preserve stable IDs for all already-assigned values. - New cases in name(), parseBuiltinObjectType(), typeOf() — symmetric with kFrameTransforms. - builtin_object_test gains typeOf coverage, the round-trip loop entry, ParsesRobotDescriptionTypeName, and RobotDescriptionRoundtripPreservesFields. 57/57 tests pass. - docs/builtin_type.md: new section explaining raw-text + format-hint rationale (open format space, no canonical codec, no embedded mesh bytes — mesh resolution is consumer-side), enum table row, conversion example for std_msgs/String on /robot_description. The producer (ParserROS in stage D) validates the text matches the declared format before emission so generic std_msgs/String topics on unrelated channels don't surface as phantom robot descriptions. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(pj_base): add frame_id to sdk::PointCloud 3D consumers (pj_scene3D's Scene3DDockWidget, RViz/Foxglove-style viewers) need the source coordinate frame to TF-transform points to a fixed render frame. Without this, a cloud emitted in the sensor frame cannot be positioned in world space when multiple frames coexist. The field is purely additive — defaulted to empty string, so existing 2D-only consumers and scalar emission are unaffected. The proto schema gets `string frame_id = 10;` at the next free wire tag; the codec round- trips it; the C++ struct picks it up in stable position. Ports commit 1a38059 from feat/robot-description-builtin onto the snake_case + codec layout of feat/builtin-protos. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(pj_base): sync C ABI builtin object enum with C++ enum PJ_builtin_object_type_t (the C ABI enum that pure-C plugins compile against for classify_schema) was stuck at the original 5 typed values plus a single commented-out reservation for OccupancyGrid. The C++ enum has since grown to 13 entries with the snake_case + AssetVideo + RobotDescription work. Without this sync, a pure-C plugin trying to declare any of OccupancyGrid / CompressedPointCloud / Mesh3D / VideoFrame / SceneEntities / AssetVideo / RobotDescription via classify_schema has no way to name the value — it would silently map to PJ_BUILTIN_OBJECT_TYPE_NONE. Adds all 7 missing typed values at their stable numeric slots (matching BuiltinObjectType in builtin_object.hpp exactly) and pins each value with a static_assert in abi_layout_sentinels_test so a future renumber breaks the build. Also drops the obsolete 5-type list from the docstring in favor of pointing readers at the C++ enum as the source of truth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Davide Faconti <dfaconti@aurynrobotics.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 2e958e1 commit b15fa35

75 files changed

Lines changed: 4870 additions & 415 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.clang-format

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,10 @@ AlignAfterOpenBracket: AlwaysBreak
2121
AllowShortBlocksOnASingleLine: Empty
2222
AllowShortFunctionsOnASingleLine: Empty
2323
AllowShortIfStatementsOnASingleLine: Never
24+
25+
---
26+
# Same column limit and indent as C++ for the protobuf schemas under pj_base/proto/.
27+
Language: Proto
28+
BasedOnStyle: Google
29+
ColumnLimit: 120
30+
IndentWidth: 2

.pre-commit-config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ repos:
1717
- id: check-xml
1818
- id: check-yaml
1919
exclude: .gitlab-ci.yml
20+
args: ['--allow-multiple-documents']
2021
- id: debug-statements
2122
- id: end-of-file-fixer
2223
exclude_types: [svg]
@@ -30,5 +31,5 @@ repos:
3031
rev: v22.1.0
3132
hooks:
3233
- id: clang-format
33-
types_or: [c++, c]
34+
types_or: [c++, c, proto]
3435
args: ['-fallback-style=none', '-i']

docs/builtin_type.md

Lines changed: 232 additions & 12 deletions
Large diffs are not rendered by default.

pj_base/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@
33
# ---------------------------------------------------------------------------
44

55
add_library(pj_base STATIC
6+
src/builtin/asset_video_codec.cpp
7+
src/builtin/compressed_point_cloud_codec.cpp
8+
src/builtin/depth_image_codec.cpp
69
src/builtin/frame_transforms_codec.cpp
710
src/builtin/image_annotations_codec.cpp
11+
src/builtin/image_codec.cpp
12+
src/builtin/mesh3d_codec.cpp
13+
src/builtin/occupancy_grid_codec.cpp
14+
src/builtin/point_cloud_codec.cpp
15+
src/builtin/scene_entities_codec.cpp
16+
src/builtin/video_frame_codec.cpp
817
src/type_tree.cpp
918
)
1019
target_include_directories(pj_base PUBLIC
@@ -63,6 +72,15 @@ if(PJ_BUILD_TESTS)
6372
tests/image_annotations_decoder_test.cpp
6473
tests/media_metadata_test.cpp
6574
tests/push_message_v2_test.cpp
75+
tests/image_codec_test.cpp
76+
tests/depth_image_codec_test.cpp
77+
tests/point_cloud_codec_test.cpp
78+
tests/compressed_point_cloud_codec_test.cpp
79+
tests/occupancy_grid_codec_test.cpp
80+
tests/mesh3d_codec_test.cpp
81+
tests/video_frame_codec_test.cpp
82+
tests/scene_entities_codec_test.cpp
83+
tests/asset_video_codec_test.cpp
6684
)
6785

6886
foreach(test_src ${PJ_BASE_TESTS})
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* @file asset_video.hpp
3+
* @brief File-backed video reference + typed playback metadata.
4+
*
5+
* AssetVideo is the entry-point handle for video assets ingested by data
6+
* loaders that point at an external media file (LeRobot datasets, MP4
7+
* loaders, etc.). Producers push exactly one AssetVideo per topic; the
8+
* ObjectStore timestamp of that entry equals `time_origin_ns` so timeline
9+
* UIs naturally see the asset's start instant.
10+
*
11+
* Unlike VideoFrame (a single frame of a streamed payload), AssetVideo
12+
* carries no pixel data — it references the file by path and surfaces
13+
* decode-routing metadata (media type, dimensions, frame rate) without
14+
* forcing the consumer to open the file just to size a playback window.
15+
*/
16+
// Copyright 2026 Davide Faconti
17+
// SPDX-License-Identifier: MIT
18+
19+
#pragma once
20+
21+
#include <cstdint>
22+
#include <optional>
23+
#include <string>
24+
25+
#include "pj_base/types.hpp"
26+
27+
namespace PJ {
28+
namespace sdk {
29+
30+
/// File-backed video reference with typed metadata.
31+
///
32+
/// `time_origin_ns` carries the wall-clock instant of the first frame.
33+
/// Consumers subtract this from a tracker time to derive a file-relative
34+
/// seek position. An absent value means the asset is not aligned to wall
35+
/// clock and should not advance with the tracker.
36+
///
37+
/// `duration_ns` is the total playable duration when known; absent means
38+
/// consumers may probe the file.
39+
///
40+
/// `media_type` is a MIME-type hint ("video/mp4", "video/x-matroska",
41+
/// "video/av1"). Empty string means "probe the file".
42+
///
43+
/// `width` / `height` are pixel dimensions; zero in either field means
44+
/// "unknown — probe the file".
45+
///
46+
/// `frame_rate` is nominal frames per second; zero or NaN means "unknown —
47+
/// probe the file". For variable-frame-rate video this is an advisory
48+
/// average; actual per-frame timestamps come from the decoder.
49+
struct AssetVideo {
50+
std::optional<Timestamp> time_origin_ns; ///< Wall-clock instant of the first frame.
51+
std::optional<int64_t> duration_ns; ///< Total playable duration in nanoseconds.
52+
std::string file_path; ///< Absolute path or path relative to a consumer-known root.
53+
std::string media_type; ///< MIME type. Empty string means "probe the file".
54+
uint32_t width = 0; ///< Pixel width. 0 means unknown.
55+
uint32_t height = 0; ///< Pixel height. 0 means unknown.
56+
double frame_rate = 0.0; ///< Nominal frames per second. 0 or NaN means unknown.
57+
};
58+
59+
} // namespace sdk
60+
} // namespace PJ
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
// Copyright 2026 Davide Faconti
3+
// SPDX-License-Identifier: MIT
4+
5+
#include <cstddef>
6+
#include <cstdint>
7+
#include <string_view>
8+
#include <vector>
9+
10+
#include "pj_base/builtin/asset_video.hpp"
11+
#include "pj_base/expected.hpp"
12+
13+
namespace PJ {
14+
15+
inline constexpr std::string_view kSchemaAssetVideo = "PJ.AssetVideo";
16+
17+
/// Serializes sdk::AssetVideo to canonical PJ.AssetVideo wire bytes (see
18+
/// pj_base/proto/pj/AssetVideo.proto).
19+
[[nodiscard]] std::vector<uint8_t> serializeAssetVideo(const sdk::AssetVideo& asset);
20+
21+
/// Decodes canonical PJ.AssetVideo wire bytes into sdk::AssetVideo.
22+
[[nodiscard]] Expected<sdk::AssetVideo> deserializeAssetVideo(const uint8_t* data, size_t size);
23+
24+
} // namespace PJ

pj_base/include/pj_base/builtin/BuiltinObject.hpp renamed to pj_base/include/pj_base/builtin/builtin_object.hpp

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @file BuiltinObject.h
2+
* @file builtin_object.hpp
33
* @brief Type-erased holder for any builtin object a MessageParser may produce.
44
*
55
* BuiltinObject is `std::any`. A producer constructs it by passing a
@@ -26,24 +26,36 @@
2626
#include <optional>
2727
#include <string_view>
2828

29-
#include "pj_base/builtin/DepthImage.hpp"
30-
#include "pj_base/builtin/FrameTransforms.hpp"
31-
#include "pj_base/builtin/Image.hpp"
32-
#include "pj_base/builtin/ImageAnnotations.hpp"
33-
#include "pj_base/builtin/PointCloud.hpp"
29+
#include "pj_base/builtin/asset_video.hpp"
30+
#include "pj_base/builtin/compressed_point_cloud.hpp"
31+
#include "pj_base/builtin/depth_image.hpp"
32+
#include "pj_base/builtin/frame_transforms.hpp"
33+
#include "pj_base/builtin/image.hpp"
34+
#include "pj_base/builtin/image_annotations.hpp"
35+
#include "pj_base/builtin/mesh3d.hpp"
36+
#include "pj_base/builtin/occupancy_grid.hpp"
37+
#include "pj_base/builtin/point_cloud.hpp"
38+
#include "pj_base/builtin/robot_description.hpp"
39+
#include "pj_base/builtin/scene_entities.hpp"
40+
#include "pj_base/builtin/video_frame.hpp"
3441

3542
namespace PJ {
3643
namespace sdk {
3744

3845
enum class BuiltinObjectType : uint16_t {
3946
kNone = 0,
40-
kImage = 1, ///< sdk::Image — raw or compressed, distinguished by encoding string.
41-
kPointCloud = 3, ///< sdk::PointCloud — packed points + per-channel field layout.
42-
kDepthImage = 4, ///< sdk::DepthImage — depth pixels + camera intrinsics.
43-
kImageAnnotations = 5, ///< sdk::ImageAnnotations — 2D overlays (points, lines, text).
44-
kFrameTransforms = 6, ///< sdk::FrameTransforms — named 3D frame relationships.
45-
// Reserved for future types; keep numeric values stable across releases.
46-
// kOccupancyGrid = 7,
47+
kImage = 1, ///< sdk::Image — raw or compressed, distinguished by encoding string.
48+
kPointCloud = 3, ///< sdk::PointCloud — packed points + per-channel field layout.
49+
kDepthImage = 4, ///< sdk::DepthImage — depth pixels + camera intrinsics.
50+
kImageAnnotations = 5, ///< sdk::ImageAnnotations — 2D overlays (points, lines, text).
51+
kFrameTransforms = 6, ///< sdk::FrameTransforms — named 3D frame relationships.
52+
kOccupancyGrid = 7, ///< sdk::OccupancyGrid — 2D metric grid (maps, costmaps).
53+
kCompressedPointCloud = 8, ///< sdk::CompressedPointCloud — opaque compressed cloud (Draco, ...).
54+
kMesh3D = 9, ///< sdk::Mesh3D — binary mesh asset (GLTF/STL/PLY/OBJ/USD/DAE).
55+
kVideoFrame = 10, ///< sdk::VideoFrame — single frame of h264/h265/vp9/av1 stream.
56+
kSceneEntities = 11, ///< sdk::SceneEntities — procedural 3D scene primitives.
57+
kAssetVideo = 12, ///< sdk::AssetVideo — file-backed video reference + playback metadata.
58+
kRobotDescription = 13, ///< sdk::RobotDescription — raw URDF/SDF/MJCF text + format hint.
4759
};
4860

4961
/// A-priori classification of a schema. Currently carries only the type;
@@ -68,6 +80,20 @@ struct SchemaClassification {
6880
return "kImageAnnotations";
6981
case BuiltinObjectType::kFrameTransforms:
7082
return "kFrameTransforms";
83+
case BuiltinObjectType::kOccupancyGrid:
84+
return "kOccupancyGrid";
85+
case BuiltinObjectType::kCompressedPointCloud:
86+
return "kCompressedPointCloud";
87+
case BuiltinObjectType::kMesh3D:
88+
return "kMesh3D";
89+
case BuiltinObjectType::kVideoFrame:
90+
return "kVideoFrame";
91+
case BuiltinObjectType::kSceneEntities:
92+
return "kSceneEntities";
93+
case BuiltinObjectType::kAssetVideo:
94+
return "kAssetVideo";
95+
case BuiltinObjectType::kRobotDescription:
96+
return "kRobotDescription";
7197
}
7298
return "kNone";
7399
}
@@ -93,6 +119,27 @@ struct SchemaClassification {
93119
if (s == "kFrameTransforms") {
94120
return BuiltinObjectType::kFrameTransforms;
95121
}
122+
if (s == "kOccupancyGrid") {
123+
return BuiltinObjectType::kOccupancyGrid;
124+
}
125+
if (s == "kCompressedPointCloud") {
126+
return BuiltinObjectType::kCompressedPointCloud;
127+
}
128+
if (s == "kMesh3D") {
129+
return BuiltinObjectType::kMesh3D;
130+
}
131+
if (s == "kVideoFrame") {
132+
return BuiltinObjectType::kVideoFrame;
133+
}
134+
if (s == "kSceneEntities") {
135+
return BuiltinObjectType::kSceneEntities;
136+
}
137+
if (s == "kAssetVideo") {
138+
return BuiltinObjectType::kAssetVideo;
139+
}
140+
if (s == "kRobotDescription") {
141+
return BuiltinObjectType::kRobotDescription;
142+
}
96143
return std::nullopt;
97144
}
98145

@@ -121,6 +168,27 @@ using BuiltinObject = std::any;
121168
if (t == typeid(FrameTransforms)) {
122169
return BuiltinObjectType::kFrameTransforms;
123170
}
171+
if (t == typeid(OccupancyGrid)) {
172+
return BuiltinObjectType::kOccupancyGrid;
173+
}
174+
if (t == typeid(CompressedPointCloud)) {
175+
return BuiltinObjectType::kCompressedPointCloud;
176+
}
177+
if (t == typeid(Mesh3D)) {
178+
return BuiltinObjectType::kMesh3D;
179+
}
180+
if (t == typeid(VideoFrame)) {
181+
return BuiltinObjectType::kVideoFrame;
182+
}
183+
if (t == typeid(SceneEntities)) {
184+
return BuiltinObjectType::kSceneEntities;
185+
}
186+
if (t == typeid(AssetVideo)) {
187+
return BuiltinObjectType::kAssetVideo;
188+
}
189+
if (t == typeid(RobotDescription)) {
190+
return BuiltinObjectType::kRobotDescription;
191+
}
124192
return BuiltinObjectType::kNone;
125193
}
126194

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* @file compressed_point_cloud.hpp
3+
* @brief Point cloud delivered in a compressed binary format (Draco, ...).
4+
*/
5+
// Copyright 2026 Davide Faconti
6+
// SPDX-License-Identifier: MIT
7+
8+
#pragma once
9+
10+
#include <cstdint>
11+
#include <string>
12+
13+
#include "pj_base/buffer_anchor.hpp"
14+
#include "pj_base/span.hpp"
15+
#include "pj_base/types.hpp"
16+
17+
namespace PJ {
18+
namespace sdk {
19+
20+
/// Point cloud delivered in a compressed binary format. Unlike PointCloud,
21+
/// the wire layout is opaque to PlotJuggler — `data` + `format` must be
22+
/// handed to the matching decoder library (e.g. Draco), which produces a
23+
/// decompressed point set on the host side.
24+
///
25+
/// This type is distinct from PointCloud because per-format decoders carry
26+
/// their own attribute table and layout semantics. Same reasoning that
27+
/// separates VideoFrame from Image.
28+
///
29+
/// `anchor` keeps the underlying buffer alive — the producer may have made
30+
/// `data` a view into the source payload (zero-copy) or into a freshly
31+
/// allocated vector; consumers don't need to know which.
32+
struct CompressedPointCloud {
33+
Timestamp timestamp_ns = 0;
34+
std::string frame_id;
35+
std::string format; ///< Codec identifier, lowercase. Recognized values include "draco".
36+
Span<const uint8_t> data;
37+
BufferAnchor anchor;
38+
};
39+
40+
} // namespace sdk
41+
} // namespace PJ
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
// Copyright 2026 Davide Faconti
3+
// SPDX-License-Identifier: MIT
4+
5+
#include <cstddef>
6+
#include <cstdint>
7+
#include <string_view>
8+
#include <vector>
9+
10+
#include "pj_base/builtin/compressed_point_cloud.hpp"
11+
#include "pj_base/expected.hpp"
12+
13+
namespace PJ {
14+
15+
inline constexpr std::string_view kSchemaCompressedPointCloud = "PJ.CompressedPointCloud";
16+
17+
/// Serializes sdk::CompressedPointCloud to canonical PJ.CompressedPointCloud
18+
/// wire bytes (see pj_base/proto/pj/CompressedPointCloud.proto).
19+
[[nodiscard]] std::vector<uint8_t> serializeCompressedPointCloud(const sdk::CompressedPointCloud& cloud);
20+
21+
/// Decodes canonical PJ.CompressedPointCloud wire bytes. The returned object
22+
/// owns its bytes via `anchor`.
23+
[[nodiscard]] Expected<sdk::CompressedPointCloud> deserializeCompressedPointCloud(const uint8_t* data, size_t size);
24+
25+
} // namespace PJ

pj_base/include/pj_base/builtin/DepthImage.hpp renamed to pj_base/include/pj_base/builtin/depth_image.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @file DepthImage.h
2+
* @file depth_image.hpp
33
* @brief Depth image with camera intrinsics.
44
*/
55
// Copyright 2026 Davide Faconti

0 commit comments

Comments
 (0)