diff --git a/src/Aardvark.Algodat.Tests/PartIndicesTests.cs b/src/Aardvark.Algodat.Tests/PartIndicesTests.cs new file mode 100644 index 0000000..2aca1d4 --- /dev/null +++ b/src/Aardvark.Algodat.Tests/PartIndicesTests.cs @@ -0,0 +1,123 @@ +/* + Copyright (C) 2006-2023. Aardvark Platform Team. http://github.com/aardvark-platform. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +using Aardvark.Base; +using Aardvark.Data.Points; +using NUnit.Framework; +using System; +using System.Collections.Generic; + +namespace Aardvark.Geometry.Tests; + +[TestFixture] +public class PartIndicesTests +{ + private static bool cmp(IReadOnlyList xs, IReadOnlyList ys) where T : IEquatable + { + if (xs.Count != ys.Count) return false; + for (var i = 0; i < xs.Count; i++) if (!xs[i].Equals(ys[i])) return false; + return true; + } + + [Test] + public void ConcatIndices_nulls() + { + { Assert.True( PartIndexUtils.ConcatIndices(first: null, firstCount: 0, second: null, secondCount: 0) is null); } + { Assert.Catch(() => PartIndexUtils.ConcatIndices(first: null, firstCount: 0, second: 1u, secondCount: 3)); } + { Assert.Catch(() => PartIndexUtils.ConcatIndices(first: 1u, firstCount: 3, second: null, secondCount: 0)); } + } + + [Test] + public void ConcatIndices_single_identical() + { + { Assert.True(PartIndexUtils.ConcatIndices(first: 1u, firstCount: 2, second: 1u, secondCount: 3) is uint x && x == 1u ); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 256u, firstCount: 2, second: 256u, secondCount: 3) is uint x && x == 256u ); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 32768u, firstCount: 2, second: 32768u, secondCount: 3) is uint x && x == 32768u); } + } + + [Test] + public void ConcatIndices_single_different() + { + { Assert.True(PartIndexUtils.ConcatIndices(first: 1u, firstCount: 2, second: 2u, secondCount: 3) is byte [] xs && cmp(xs, new byte [] { 1, 1, 2, 2, 2 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 1u, firstCount: 2, second: 257u, secondCount: 3) is short[] xs && cmp(xs, new short[] { 1, 1, 257, 257, 257 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 1u, firstCount: 2, second: 32769u, secondCount: 3) is int [] xs && cmp(xs, new int [] { 1, 1, 32769, 32769, 32769 })); } + + { Assert.True(PartIndexUtils.ConcatIndices(first: 256u, firstCount: 2, second: 2u, secondCount: 3) is short[] xs && cmp(xs, new short[] { 256, 256, 2, 2, 2 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 256u, firstCount: 2, second: 257u, secondCount: 3) is short[] xs && cmp(xs, new short[] { 256, 256, 257, 257, 257 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 256u, firstCount: 2, second: 32769u, secondCount: 3) is int [] xs && cmp(xs, new int [] { 256, 256, 32769, 32769, 32769 })); } + + { Assert.True(PartIndexUtils.ConcatIndices(first: 32768u, firstCount: 2, second: 2u, secondCount: 3) is int [] xs && cmp(xs, new int [] { 32768, 32768, 2, 2, 2 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 32768u, firstCount: 2, second: 257u, secondCount: 3) is int [] xs && cmp(xs, new int [] { 32768, 32768, 257, 257, 257 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 32768u, firstCount: 2, second: 32769u, secondCount: 3) is int [] xs && cmp(xs, new int [] { 32768, 32768, 32769, 32769, 32769 })); } + } + + + [Test] + public void ConcatIndices_single_and_array() + { + { Assert.True(PartIndexUtils.ConcatIndices(first: 1u, firstCount: 2, second: new byte [] { 2, 3, 4 }, secondCount: 3) is byte [] xs && cmp(xs, new byte [] { 1, 1, 2, 3, 4 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 1u, firstCount: 2, second: new short[] { 2, 3, 4 }, secondCount: 3) is short[] xs && cmp(xs, new short[] { 1, 1, 2, 3, 4 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 1u, firstCount: 2, second: new int [] { 2, 3, 4 }, secondCount: 3) is int [] xs && cmp(xs, new int [] { 1, 1, 2, 3, 4 })); } + + { Assert.True(PartIndexUtils.ConcatIndices(first: 256u, firstCount: 2, second: new byte [] { 2, 3, 4 }, secondCount: 3) is short[] xs && cmp(xs, new short[] { 256, 256, 2, 3, 4 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 256u, firstCount: 2, second: new short[] { 2, 3, 4 }, secondCount: 3) is short[] xs && cmp(xs, new short[] { 256, 256, 2, 3, 4 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 256u, firstCount: 2, second: new int [] { 2, 3, 4 }, secondCount: 3) is int [] xs && cmp(xs, new int [] { 256, 256, 2, 3, 4 })); } + + { Assert.True(PartIndexUtils.ConcatIndices(first: 32768u, firstCount: 2, second: new byte [] { 2, 3, 4 }, secondCount: 3) is int [] xs && cmp(xs, new int [] { 32768, 32768, 2, 3, 4 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 32768u, firstCount: 2, second: new short[] { 2, 3, 4 }, secondCount: 3) is int [] xs && cmp(xs, new int [] { 32768, 32768, 2, 3, 4 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: 32768u, firstCount: 2, second: new int [] { 2, 3, 4 }, secondCount: 3) is int [] xs && cmp(xs, new int [] { 32768, 32768, 2, 3, 4 })); } + } + + [Test] + public void ConcatIndices_array_array() + { + { Assert.True(PartIndexUtils.ConcatIndices(first: new byte [] { 2, 3 }, firstCount: 2, second: new byte [] { 4, 5 }, secondCount: 2) is byte [] xs && cmp(xs, new byte [] { 2, 3, 4, 5 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: new byte [] { 2, 3 }, firstCount: 2, second: new short[] { 4, 5 }, secondCount: 2) is short[] xs && cmp(xs, new short[] { 2, 3, 4, 5 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: new byte [] { 2, 3 }, firstCount: 2, second: new int [] { 4, 5 }, secondCount: 2) is int [] xs && cmp(xs, new int [] { 2, 3, 4, 5 })); } + + { Assert.True(PartIndexUtils.ConcatIndices(first: new short[] { 2, 3 }, firstCount: 2, second: new byte [] { 4, 5 }, secondCount: 2) is short[] xs && cmp(xs, new short[] { 2, 3, 4, 5 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: new short[] { 2, 3 }, firstCount: 2, second: new short[] { 4, 5 }, secondCount: 2) is short[] xs && cmp(xs, new short[] { 2, 3, 4, 5 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: new short[] { 2, 3 }, firstCount: 2, second: new int [] { 4, 5 }, secondCount: 2) is int [] xs && cmp(xs, new int [] { 2, 3, 4, 5 })); } + + { Assert.True(PartIndexUtils.ConcatIndices(first: new int [] { 2, 3 }, firstCount: 2, second: new byte [] { 4, 5 }, secondCount: 2) is int [] xs && cmp(xs, new int [] { 2, 3, 4, 5 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: new int [] { 2, 3 }, firstCount: 2, second: new short[] { 4, 5 }, secondCount: 2) is int [] xs && cmp(xs, new int [] { 2, 3, 4, 5 })); } + { Assert.True(PartIndexUtils.ConcatIndices(first: new int [] { 2, 3 }, firstCount: 2, second: new int [] { 4, 5 }, secondCount: 2) is int [] xs && cmp(xs, new int [] { 2, 3, 4, 5 })); } + } + + [Test] + public void ExtendRangeBy() + { + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), 8u) == new Range1i(7, 11)); + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), 1u) == new Range1i(1, 11)); + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), 42u) == new Range1i(7, 42)); + + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), 8) == new Range1i(7, 11)); + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), 1) == new Range1i(1, 11)); + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), 42) == new Range1i(7, 42)); + + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), new byte[] { 2, 12 }) == new Range1i(2, 12)); + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), new byte[] { 8, 10 }) == new Range1i(7, 11)); + + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), new short[] { 2, 12 }) == new Range1i(2, 12)); + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), new short[] { 8, 10 }) == new Range1i(7, 11)); + + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), new int[] { 2, 12 }) == new Range1i(2, 12)); + Assert.True(PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), new int[] { 8, 10 }) == new Range1i(7, 11)); + } + + [Test] + public void ExtendRangeBy_Fail() + { + Assert.Catch(() => PartIndexUtils.ExtendRangeBy(new Range1i(7, 11), null!)); + } +} diff --git a/src/Aardvark.Data.Points.Base/Chunk.cs b/src/Aardvark.Data.Points.Base/Chunk.cs index a7cde68..20e2d85 100644 --- a/src/Aardvark.Data.Points.Base/Chunk.cs +++ b/src/Aardvark.Data.Points.Base/Chunk.cs @@ -161,7 +161,7 @@ public static Chunk ImmutableMerge(Chunk a, Chunk b) } } - return new Chunk(ps, cs, ns, js, ks, PartIndexUtils.MergeIndices(a.PartIndices, a.Count, b.PartIndices, b.Count), new Box3d(a.BoundingBox, b.BoundingBox)); + return new Chunk(ps, cs, ns, js, ks, PartIndexUtils.ConcatIndices(a.PartIndices, a.Count, b.PartIndices, b.Count), new Box3d(a.BoundingBox, b.BoundingBox)); } public static Chunk ImmutableMerge(params Chunk[] chunks) @@ -196,7 +196,7 @@ public static Chunk ImmutableMerge(params Chunk[] chunks) if (ps == null) throw new Exception("Invariant 4cc7d585-9a46-4ba2-892a-95fce9ed06da."); return new Chunk(ps, cs, ns, js, ks, - partIndices: PartIndexUtils.MergeIndices(chunks.Select(x => (indices: x.PartIndices, count: x.Count))), + partIndices: PartIndexUtils.ConcatIndices(chunks.Select(x => (indices: x.PartIndices, count: x.Count))), bbox: new Box3d(chunks.Select(x => x.BoundingBox)) ); } @@ -378,7 +378,7 @@ public Chunk Union(Chunk other) Append(Normals, other.Normals), Append(Intensities, other.Intensities), Append(Classifications, other.Classifications), - partIndices: PartIndexUtils.MergeIndices(PartIndices, Count, other.PartIndices, other.Count), + partIndices: PartIndexUtils.ConcatIndices(PartIndices, Count, other.PartIndices, other.Count), Box.Union(BoundingBox, other.BoundingBox) ); } diff --git a/src/Aardvark.Data.Points.Base/PartIndexUtils.cs b/src/Aardvark.Data.Points.Base/PartIndexUtils.cs index ea54ff9..6eaf397 100644 --- a/src/Aardvark.Data.Points.Base/PartIndexUtils.cs +++ b/src/Aardvark.Data.Points.Base/PartIndexUtils.cs @@ -18,6 +18,7 @@ limitations under the License. using Aardvark.Base; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; #pragma warning disable CS1591 @@ -30,14 +31,15 @@ namespace Aardvark.Data.Points; public static class PartIndexUtils { /// - /// Merges part indices (null, uint, [byte|short|int] array). + /// Concatenates part indices (uint, [byte|short|int] array). /// - public static object? MergeIndices(object? first, int firstCount, object? second, int secondCount) + public static object? ConcatIndices( + object? first , int firstCount, + object? second, int secondCount + ) { - // expect: both ranges are defined, or both ranges are null - if ((first == null && second != null) || (first != null && second == null)) throw new Exception( - "Invariant 874c2220-4779-41f0-9f3e-1c0ef4988da9." - ); + // expect: both defined, or both null + if ((first != null && second == null) || (first == null && second != null)) throw new Exception("Invariant 7e8345fc-c993-48fd-9862-33c9928aba3f."); checked { @@ -49,17 +51,29 @@ public static class PartIndexUtils (uint x, _ ) => second switch { - IReadOnlyList ys when x <= byte .MaxValue => createArray2((byte )x, firstCount, ys), - IReadOnlyList ys when x <= short.MaxValue => createArray2((short)x, firstCount, ys), - IReadOnlyList ys when x <= int .MaxValue => createArray2((int )x, firstCount, ys), + IReadOnlyList ys when x <= byte .MaxValue => createArray2((byte )x, firstCount, ys ), + IReadOnlyList ys when x <= short.MaxValue => createArray2((short)x, firstCount, b2s(ys)), + IReadOnlyList ys when x <= int .MaxValue => createArray2((int )x, firstCount, b2i(ys)), + + IReadOnlyList ys when x <= short.MaxValue => createArray2((short)x, firstCount, ys ), + IReadOnlyList ys when x <= int .MaxValue => createArray2((int )x, firstCount, s2i(ys)), + + IReadOnlyList ys when x <= int .MaxValue => createArray2((int )x, firstCount, ys ), + _ => throw new Exception("Invariant 588fea29-4daa-4356-92a4-369f64ac5778.") }, - (_ , uint y) => second switch + (_ , uint y) => first switch { - IReadOnlyList xs when y <= byte .MaxValue => createArray3(xs, (byte )y, secondCount), - IReadOnlyList xs when y <= short.MaxValue => createArray3(xs, (short)y, secondCount), + IReadOnlyList xs when y <= byte .MaxValue => createArray3( xs , (byte )y, secondCount), + IReadOnlyList xs when y <= short.MaxValue => createArray3(b2s(xs), (short)y, secondCount), + IReadOnlyList xs when y <= int .MaxValue => createArray3(b2i(xs), (int )y, secondCount), + + IReadOnlyList xs when y <= short.MaxValue => createArray3( xs , (short)y, secondCount), + IReadOnlyList xs when y <= int .MaxValue => createArray3(s2i(xs), (int )y, secondCount), + IReadOnlyList xs when y <= int .MaxValue => createArray3(xs, (int )y, secondCount), + _ => throw new Exception("Invariant 7ddfc8c0-2e66-45ef-94a9-31d21f6009f9.") }, @@ -136,98 +150,28 @@ object createArray4(IReadOnlyList first, IReadOnlyList second) where T } /// - /// Merges part indices (null, uint, [byte|short|int] array). + /// Concatenates part indices (null, uint, [byte|short|int] array). /// - public static object? MergeIndices(IEnumerable<(object? indices, int count)> xs) + public static object? ConcatIndices(IEnumerable<(object? indices, int count)> xs) { var (resultIndices, resultCount) = xs.FirstOrDefault(); foreach (var (xIndices, xCount) in xs.Skip(1)) { - MergeIndices(resultIndices, resultCount, xIndices, xCount); + ConcatIndices(resultIndices, resultCount, xIndices, xCount); resultCount += xCount; } return resultIndices; } - /// - /// Merges part index ranges (null, (u)int, Range1[bsi]). - /// - public static object? MergeRanges(object? first, object? second) - { - checked - { - return (first, second) switch - { - (null, null) => null, - (uint x, null) => x, - (int x, null) => x, - (Range1b x, null) => x, - (Range1s x, null) => x, - (Range1i x, null) => x, - (null, uint y) => y, - (null, int y) => y, - (null, Range1b y) => y, - (null, Range1s y) => y, - (null, Range1i y) => y, - (uint x, uint y) => (x == y) ? x : new Range1i(new[] { (int)x, (int)y }), - - (uint x, IList ys) => ((Range1i)new Range1b(ys)).ExtendedBy((int)x), - (uint x, IList ys) => ((Range1i)new Range1s(ys)).ExtendedBy((int)x), - (uint x, IList ys) => new Range1i(ys).ExtendedBy((int)x), - - //(IList xs, uint y) => ((Range1i)new Range1b(xs)).ExtendedBy((int)y), - //(IList xs, uint y) => ((Range1i)new Range1s(xs)).ExtendedBy((int)y), - //(IList xs, uint y) => new Range1i(xs).ExtendedBy((int)y), - - //(IList xs, IList ys) => (Range1i)new Range1b(xs.Concat(ys)), - //(IList xs, IList ys) => (Range1i)new Range1s(xs.Select(x => (short)x).Concat(ys)), - //(IList xs, IList ys) => new Range1i(xs.Select(x => (int)x).Concat(ys)), - - //(IList xs, IList ys) => (Range1i)new Range1s(xs.Concat(ys.Select(x => (short)x))), - //(IList xs, IList ys) => (Range1i)new Range1s(xs.Concat(ys)), - //(IList xs, IList ys) => new Range1i(xs.Select(x => (int)x).Concat(ys)), - - //(IList xs, IList ys) => new Range1i(xs.Concat(ys.Select(x => (int)x))), - //(IList xs, IList ys) => new Range1i(xs.Concat(ys.Select(x => (int)x))), - //(IList xs, IList ys) => new Range1i(xs.Concat(ys)), - - _ => throw new Exception( - $"Unexpected part indices types {first?.GetType().FullName ?? "null"} and {second?.GetType().FullName ?? "null"}. " + - $"Error 2f0672f5-8c6b-400b-8172-e83a30d70c28" - ) - }; - } - } - - /// - /// Merges multiple part index ranges (uint, IList of [byte|short|int]). - /// - public static object? MergeRanges(params object?[] xs) - { - if (xs.Length == 0) return null; - - var result = xs[0]; - for (var i = 1; i < xs.Length; i++) result = MergeRanges(result, xs[i]); - return result; - } - - /// - /// Merges multiple part index ranges (uint, IList of [byte|short|int]). - /// - public static object? MergeRanges(IEnumerable xs) + public static Range1i ExtendRangeBy(in Range1i range, object partIndices) { - var result = xs.FirstOrDefault(); - foreach (var x in xs.Skip(1)) result = MergeRanges(result, x); - return result; - } + if (partIndices == null) throw new Exception("Invariant d781e171-41c3-4272-88a7-261cea302c18."); - public static Range1i ExtendRangeBy(in Range1i range, object? partIndices) - { checked { return partIndices switch { - null => range, + int x => range.ExtendedBy(x), uint x => range.ExtendedBy((int)x), IList xs => range.ExtendedBy((Range1i)new Range1b(xs)), IList xs => range.ExtendedBy((Range1i)new Range1s(xs)), diff --git a/src/Aardvark.Geometry.PointSet/Octrees/Merge.cs b/src/Aardvark.Geometry.PointSet/Octrees/Merge.cs index a426ab9..df60ffe 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/Merge.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/Merge.cs @@ -802,7 +802,7 @@ private static IPointCloudNode MergeLeafAndLeafWithIdenticalRootCell(IPointCloud var ns = Concat(a.Normals?.Value, b.Normals?.Value); var js = Concat(a.Intensities?.Value, b.Intensities?.Value); var ks = Concat(a.Classifications?.Value, b.Classifications?.Value); - var qs = PartIndexUtils.MergeIndices(a.PartIndices, a.PointCountCell, b.PartIndices, b.PointCountCell); + var qs = PartIndexUtils.ConcatIndices(a.PartIndices, a.PointCountCell, b.PartIndices, b.PointCountCell); var chunk = new Chunk(ps, cs, ns, js, ks, partIndices: qs, cell.BoundingBox); if (config.NormalizePointDensityGlobal)