/* * 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 CartographerSharp.Models.Transform; using CartographerSharp.Sensor; using CartographerSharp.Transform; using System; using RobotNet10.Shared.Numbers; namespace CartographerSharp.Mapping.D2D; /// /// 2D Submap implementation. /// public class Submap2D : Submap { private Grid2D? _grid; private Grid2D? _highResGrid; private readonly ValueConversionTables _conversionTables; public Submap2D(Rigid3d localSubmapPose, Grid2D grid, ValueConversionTables conversionTables, Grid2D? highResGrid = null) : base(localSubmapPose) // Pass the full Rigid3d pose including rotation { _grid = grid; _highResGrid = highResGrid; _conversionTables = conversionTables; } // Keep old constructor for backward compatibility (if needed) public Submap2D(Vector2 origin, Grid2D grid, ValueConversionTables conversionTables) : this(new Rigid3d(new Vector3(origin.X, origin.Y, 0.0), Quaternion.Identity), grid, conversionTables) { } public Submap2D(Models.Mapping.Submap2D proto, ValueConversionTables conversionTables) : base((Rigid3d)proto.LocalPose) { _conversionTables = conversionTables; NumRangeData = proto.NumRangeData; InsertionFinished = proto.Finished; // Match C++: if (proto.has_grid()) - no resolution check if (proto.Grid.HasValue) { var gridProto = proto.Grid.Value; if (gridProto.ProbabilityGrid2D.HasValue) { _grid = new ProbabilityGrid(gridProto, conversionTables); } else if (gridProto.Tsdf2D.HasValue) { _grid = new TSDF2D(gridProto, conversionTables); } else { throw new ArgumentException("proto::Submap2D has grid with unknown type.", nameof(proto)); } } } /// /// Gets the grid. /// public Grid2D? Grid => _grid; /// /// Gets the optional high resolution grid for fine-grained Ceres scan matching. /// public Grid2D? HighResGrid => _highResGrid; /// /// Insert 'range_data' into this submap using 'range_data_inserter'. The /// submap must not be finished yet. /// public void InsertRangeData(RangeData rangeData, IRangeDataInserter rangeDataInserter) { if (InsertionFinished) { throw new InvalidOperationException("Cannot insert range data into finished submap"); } if (_grid == null) { throw new InvalidOperationException("Grid is not initialized"); } // Insert range data directly (already transformed to submap frame in ActiveSubmaps2D) if (_highResGrid != null) { // Main grid and high-res grid are independent - insert in parallel. var highResGrid = _highResGrid; var highResTask = Task.Run(() => rangeDataInserter.Insert(rangeData, (IGrid)highResGrid)); rangeDataInserter.Insert(rangeData, (IGrid)_grid); highResTask.GetAwaiter().GetResult(); } else { rangeDataInserter.Insert(rangeData, (IGrid)_grid); } NumRangeData++; } /// /// Finishes the submap. /// Match C++: grid_ = grid_->ComputeCroppedGrid(); to reduce memory usage /// public void Finish() { if (_grid == null) { throw new InvalidOperationException("Grid is not initialized"); } if (InsertionFinished) { throw new InvalidOperationException("Submap is already finished"); } // Match C++: Crop grid to reduce memory usage when submap is finished _grid = _grid.ComputeCroppedGrid(); _highResGrid = _highResGrid?.ComputeCroppedGrid(); InsertionFinished = true; } /// /// Converts to proto representation. /// public override Models.Mapping.Submap ToProto(bool includeGridData) { var submap2D = new Models.Mapping.Submap2D( (Rigid3dProto)LocalPose, NumRangeData, InsertionFinished, includeGridData && _grid != null ? _grid.ToProto() : null ); // Note: SubmapId will be set by caller // Note: SubmapId will be set by caller when creating full Submap return new Models.Mapping.Submap(new Models.Mapping.PoseGraph.SubmapId(0, 0), submap2D, null); } /// /// Updates from proto representation. /// Match C++: CHECK(proto.has_submap_2d()) /// public override void UpdateFromProto(Models.Mapping.Submap proto) { if (!proto.Submap2D.HasValue) { throw new ArgumentException("Proto must contain Submap2D", nameof(proto)); } var submap2D = proto.Submap2D.Value; NumRangeData = submap2D.NumRangeData; InsertionFinished = submap2D.Finished; // Match C++: if (proto.submap_2d().has_grid()) - no resolution check if (submap2D.Grid.HasValue) { var gridProto = submap2D.Grid.Value; if (gridProto.ProbabilityGrid2D.HasValue) { _grid = new ProbabilityGrid(gridProto, _conversionTables); } else if (gridProto.Tsdf2D.HasValue) { _grid = new TSDF2D(gridProto, _conversionTables); } else { throw new ArgumentException("proto::Submap2D has grid with unknown type.", nameof(proto)); } } } }