/* * Copyright 2017 The Cartographer Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using RobotNet10.Shared.Numbers; using CartographerSharp.Models.Sensor; using CartographerSharp.Models.Transform; namespace CartographerSharp.Sensor; /// /// Timed point cloud data structure. /// public struct TimedPointCloudData { public long Time { get; set; } public Vector3 Origin { get; set; } public TimedPointCloud Ranges { get; set; } /// /// 'intensities' has to be same size as 'ranges', or empty. /// public List Intensities { get; set; } public TimedPointCloudData(long time, Vector3 origin, TimedPointCloud ranges, List? intensities = null) { Time = time; Origin = origin; Ranges = ranges; Intensities = intensities ?? []; if (Intensities.Count > 0 && Intensities.Count != Ranges.Count) { throw new ArgumentException("Intensities must have the same size as ranges, or be empty."); } } } /// /// Timed point cloud origin data structure. /// public struct TimedPointCloudOriginData(long time, List origins, List ranges) { /// /// Range measurement with point time, intensity, and origin index. /// public struct RangeMeasurement(TimedRangefinderPoint pointTime, double intensity, int originIndex) { public TimedRangefinderPoint PointTime { get; set; } = pointTime; public double Intensity { get; set; } = intensity; public int OriginIndex { get; set; } = originIndex; } public long Time { get; set; } = time; public List Origins { get; set; } = origins; public List Ranges { get; set; } = ranges; } /// /// Operations on TimedPointCloudData. /// public static class TimedPointCloudDataOperations { /// /// Converts 'timed_point_cloud_data' to a proto::TimedPointCloudData. /// Note: Time is already in Universal Time Scale ticks (long), so no conversion needed. /// public static Models.Sensor.TimedPointCloudData ToProto(TimedPointCloudData timedPointCloudData) { var pointData = new List(timedPointCloudData.Ranges.Count); foreach (var range in timedPointCloudData.Ranges) { pointData.Add(new Models.Sensor.TimedRangefinderPoint( new Vector3f(range.Position.X, range.Position.Y, range.Position.Z), range.Time )); } return new Models.Sensor.TimedPointCloudData( timedPointCloudData.Time, // Already in Universal Time Scale ticks new Vector3f(timedPointCloudData.Origin.X, timedPointCloudData.Origin.Y, timedPointCloudData.Origin.Z), pointDataLegacy: null, pointData: pointData, intensities: timedPointCloudData.Intensities ); } /// /// Converts 'proto' to TimedPointCloudData. /// Note: Time is already in Universal Time Scale ticks (long), so no conversion needed. /// public static TimedPointCloudData FromProto(Models.Sensor.TimedPointCloudData proto) { var timedPointCloud = new TimedPointCloud(); // Use point_data if available, otherwise fall back to point_data_legacy if (proto.PointData != null && proto.PointData.Count > 0) { timedPointCloud.Capacity = proto.PointData.Count; foreach (var protoPoint in proto.PointData) { timedPointCloud.Add(new TimedRangefinderPoint( new Vector3(protoPoint.Position.X, protoPoint.Position.Y, protoPoint.Position.Z), protoPoint.Time )); } } else if (proto.PointDataLegacy != null && proto.PointDataLegacy.Count > 0) { // Legacy format: Vector4f where T component is time timedPointCloud.Capacity = proto.PointDataLegacy.Count; foreach (var point4 in proto.PointDataLegacy) { timedPointCloud.Add(new TimedRangefinderPoint( new Vector3(point4.X, point4.Y, point4.Z), point4.T )); } } var intensities = new List(proto.Intensities ?? []); if (intensities.Count > 0 && intensities.Count != timedPointCloud.Count) { throw new ArgumentException("Intensities size must match ranges size, or be empty."); } return new TimedPointCloudData( proto.Timestamp, // Already in Universal Time Scale ticks new Vector3(proto.Origin.X, proto.Origin.Y, proto.Origin.Z), timedPointCloud, intensities ); } }