Skip to content

Commit

Permalink
PartIndexUtils: merge indices and ranges, tests and fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanmaierhofer committed Oct 5, 2023
1 parent ce6e6db commit a86b48f
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 92 deletions.
123 changes: 123 additions & 0 deletions src/Aardvark.Algodat.Tests/PartIndicesTests.cs
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
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<T>(IReadOnlyList<T> xs, IReadOnlyList<T> ys) where T : IEquatable<T>
{
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!));
}
}
6 changes: 3 additions & 3 deletions src/Aardvark.Data.Points.Base/Chunk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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))
);
}
Expand Down Expand Up @@ -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)
);
}
Expand Down
120 changes: 32 additions & 88 deletions src/Aardvark.Data.Points.Base/PartIndexUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,14 +31,15 @@ namespace Aardvark.Data.Points;
public static class PartIndexUtils
{
/// <summary>
/// Merges part indices (null, uint, [byte|short|int] array).
/// Concatenates part indices (uint, [byte|short|int] array).
/// </summary>
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
{
Expand All @@ -49,17 +51,29 @@ public static class PartIndexUtils

(uint x, _ ) => second switch
{
IReadOnlyList<byte > ys when x <= byte .MaxValue => createArray2((byte )x, firstCount, ys),
IReadOnlyList<short> ys when x <= short.MaxValue => createArray2((short)x, firstCount, ys),
IReadOnlyList<int > ys when x <= int .MaxValue => createArray2((int )x, firstCount, ys),
IReadOnlyList<byte > ys when x <= byte .MaxValue => createArray2((byte )x, firstCount, ys ),
IReadOnlyList<byte > ys when x <= short.MaxValue => createArray2((short)x, firstCount, b2s(ys)),
IReadOnlyList<byte > ys when x <= int .MaxValue => createArray2((int )x, firstCount, b2i(ys)),

IReadOnlyList<short> ys when x <= short.MaxValue => createArray2((short)x, firstCount, ys ),
IReadOnlyList<short> ys when x <= int .MaxValue => createArray2((int )x, firstCount, s2i(ys)),

IReadOnlyList<int > 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<byte > xs when y <= byte .MaxValue => createArray3(xs, (byte )y, secondCount),
IReadOnlyList<short> xs when y <= short.MaxValue => createArray3(xs, (short)y, secondCount),
IReadOnlyList<byte > xs when y <= byte .MaxValue => createArray3( xs , (byte )y, secondCount),
IReadOnlyList<byte > xs when y <= short.MaxValue => createArray3(b2s(xs), (short)y, secondCount),
IReadOnlyList<byte > xs when y <= int .MaxValue => createArray3(b2i(xs), (int )y, secondCount),

IReadOnlyList<short> xs when y <= short.MaxValue => createArray3( xs , (short)y, secondCount),
IReadOnlyList<short> xs when y <= int .MaxValue => createArray3(s2i(xs), (int )y, secondCount),

IReadOnlyList<int > xs when y <= int .MaxValue => createArray3(xs, (int )y, secondCount),

_ => throw new Exception("Invariant 7ddfc8c0-2e66-45ef-94a9-31d21f6009f9.")
},

Expand Down Expand Up @@ -136,98 +150,28 @@ object createArray4<T>(IReadOnlyList<T> first, IReadOnlyList<T> second) where T
}

/// <summary>
/// Merges part indices (null, uint, [byte|short|int] array).
/// Concatenates part indices (null, uint, [byte|short|int] array).
/// </summary>
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;
}

/// <summary>
/// Merges part index ranges (null, (u)int, Range1[bsi]).
/// </summary>
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<byte> ys) => ((Range1i)new Range1b(ys)).ExtendedBy((int)x),
(uint x, IList<short> ys) => ((Range1i)new Range1s(ys)).ExtendedBy((int)x),
(uint x, IList<int> ys) => new Range1i(ys).ExtendedBy((int)x),

//(IList<byte> xs, uint y) => ((Range1i)new Range1b(xs)).ExtendedBy((int)y),
//(IList<short> xs, uint y) => ((Range1i)new Range1s(xs)).ExtendedBy((int)y),
//(IList<int> xs, uint y) => new Range1i(xs).ExtendedBy((int)y),

//(IList<byte> xs, IList<byte> ys) => (Range1i)new Range1b(xs.Concat(ys)),
//(IList<byte> xs, IList<short> ys) => (Range1i)new Range1s(xs.Select(x => (short)x).Concat(ys)),
//(IList<byte> xs, IList<int> ys) => new Range1i(xs.Select(x => (int)x).Concat(ys)),

//(IList<short> xs, IList<byte> ys) => (Range1i)new Range1s(xs.Concat(ys.Select(x => (short)x))),
//(IList<short> xs, IList<short> ys) => (Range1i)new Range1s(xs.Concat(ys)),
//(IList<short> xs, IList<int> ys) => new Range1i(xs.Select(x => (int)x).Concat(ys)),

//(IList<int> xs, IList<byte> ys) => new Range1i(xs.Concat(ys.Select(x => (int)x))),
//(IList<int> xs, IList<short> ys) => new Range1i(xs.Concat(ys.Select(x => (int)x))),
//(IList<int> xs, IList<int> 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"
)
};
}
}

/// <summary>
/// Merges multiple part index ranges (uint, IList of [byte|short|int]).
/// </summary>
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;
}

/// <summary>
/// Merges multiple part index ranges (uint, IList of [byte|short|int]).
/// </summary>
public static object? MergeRanges(IEnumerable<object?> 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<byte> xs => range.ExtendedBy((Range1i)new Range1b(xs)),
IList<short> xs => range.ExtendedBy((Range1i)new Range1s(xs)),
Expand Down
2 changes: 1 addition & 1 deletion src/Aardvark.Geometry.PointSet/Octrees/Merge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit a86b48f

Please sign in to comment.