/* * Copyright 2016 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.Transform; namespace CartographerSharp.Sensor; /// /// Stores 3D positions of points together with some additional data, e.g. intensities. /// public class PointCloud { private readonly List _points; private readonly List _intensities; /// /// Creates an empty point cloud. /// public PointCloud() { _points = []; _intensities = []; } /// /// Creates a point cloud from points. /// public PointCloud(IEnumerable points) { _points = [.. points]; _intensities = []; } /// /// Creates a point cloud from points and intensities. /// public PointCloud(IEnumerable points, IEnumerable intensities) { _points = [.. points]; _intensities = [.. intensities]; if (_intensities.Count > 0 && _intensities.Count != _points.Count) { throw new ArgumentException("Intensities must have the same size as points, or be empty."); } } /// /// Returns the number of points in the point cloud. /// public int Count => _points.Count; /// /// Checks whether there are any points in the point cloud. /// public bool IsEmpty => _points.Count == 0; /// /// Gets the points in the point cloud. /// public IReadOnlyList Points => _points; /// /// Gets the intensities in the point cloud. /// public IReadOnlyList Intensities => _intensities; /// /// Gets a point at the specified index. /// public RangefinderPoint this[int index] => _points[index]; /// /// Adds a point to the point cloud. /// public void Add(RangefinderPoint point) { _points.Add(point); } /// /// Creates a PointCloud consisting of all the points for which predicate returns true, /// together with the corresponding intensities. /// public PointCloud CopyIf(Func predicate) { var points = new List(); var intensities = new List(); if (_intensities.Count == 0) { foreach (var point in _points) { if (predicate(point)) { points.Add(point); } } } else { for (int i = 0; i < _points.Count; i++) { var point = _points[i]; if (predicate(point)) { points.Add(point); intensities.Add(_intensities[i]); } } } return new PointCloud(points, intensities); } /// /// Gets an enumerator for the points. /// public IEnumerator GetEnumerator() { return _points.GetEnumerator(); } } /// /// Stores 3D positions of points with their relative measurement time in the /// fourth entry. Time is in seconds, increasing and relative to the moment when /// the last point was acquired. So, the fourth entry for the last point is 0.f. /// If timing is not available, all fourth entries are 0.f. For 2D points, the /// third entry is 0.f (and the fourth entry is time). /// public class TimedPointCloud : List { public TimedPointCloud() : base() { } public TimedPointCloud(int capacity) : base(capacity) { } public TimedPointCloud(IEnumerable collection) : base(collection) { } } /// /// Retained for compatibility. Contains timed point cloud with intensities. /// public struct PointCloudWithIntensities(TimedPointCloud points, List intensities) { public TimedPointCloud Points { get; set; } = points; public List Intensities { get; set; } = intensities; } /// /// Transforms a point cloud according to a transform. /// public static class PointCloudOperations { /// /// Transforms 'point_cloud' according to 'transform'. /// public static PointCloud Transform(PointCloud pointCloud, Rigid3f transform) { var points = new List(pointCloud.Count); foreach (var point in pointCloud.Points) { points.Add(transform * point); } return new PointCloud(points, pointCloud.Intensities); } /// /// Transforms 'point_cloud' according to 'transform'. /// public static TimedPointCloud Transform(TimedPointCloud pointCloud, Rigid3f transform) { var result = new TimedPointCloud(pointCloud.Count); foreach (var point in pointCloud) { result.Add(transform * point); } return result; } /// /// Returns a new point cloud without points that fall outside the region defined /// by 'min_z' and 'max_z'. /// public static PointCloud Crop(PointCloud pointCloud, double minZ, double maxZ) { return pointCloud.CopyIf(point => minZ <= point.Position.Z && point.Position.Z <= maxZ); } /// /// Translates all points in the point cloud by the specified offset. /// public static PointCloud Translate(PointCloud pointCloud, Vector3 offset) { var points = new List(pointCloud.Count); foreach (var point in pointCloud.Points) { points.Add(new RangefinderPoint { Position = point.Position + offset }); } return new PointCloud(points, pointCloud.Intensities); } }