diff --git a/src/Aardvark.Data.Points.Base/Chunk.cs b/src/Aardvark.Data.Points.Base/Chunk.cs index 6641caa..a7cde68 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.Union(a.PartIndices, b.PartIndices), new Box3d(a.BoundingBox, b.BoundingBox)); + return new Chunk(ps, cs, ns, js, ks, PartIndexUtils.MergeIndices(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.Union(chunks.Select(x => x.PartIndices)), + partIndices: PartIndexUtils.MergeIndices(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.Union(PartIndices, other.PartIndices), + partIndices: PartIndexUtils.MergeIndices(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 681c4c9..ea54ff9 100644 --- a/src/Aardvark.Data.Points.Base/PartIndexUtils.cs +++ b/src/Aardvark.Data.Points.Base/PartIndexUtils.cs @@ -30,41 +30,169 @@ namespace Aardvark.Data.Points; public static class PartIndexUtils { /// - /// Returns union of two part indices (uint, IList of [byte|short|int]). + /// Merges part indices (null, uint, [byte|short|int] array). /// - public static object? Union(object? first, object? second) + public static object? MergeIndices(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." + ); + + checked + { + return (first, second) switch + { + (null , null ) => null, + + (uint x, uint y) => (x == y) ? x : createArray1(x, firstCount, y, secondCount), + + (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), + _ => throw new Exception("Invariant 588fea29-4daa-4356-92a4-369f64ac5778.") + }, + + (_ , uint y) => second 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 <= int .MaxValue => createArray3(xs, (int )y, secondCount), + _ => throw new Exception("Invariant 7ddfc8c0-2e66-45ef-94a9-31d21f6009f9.") + }, + + (IReadOnlyList xs, IReadOnlyList ys) => createArray4( xs , ys ), + (IReadOnlyList xs, IReadOnlyList ys) => createArray4(b2s(xs), ys ), + (IReadOnlyList xs, IReadOnlyList ys) => createArray4(b2i(xs), ys ), + (IReadOnlyList xs, IReadOnlyList ys) => createArray4( xs , b2s(ys)), + (IReadOnlyList xs, IReadOnlyList ys) => createArray4( xs , ys ), + (IReadOnlyList xs, IReadOnlyList ys) => createArray4(s2i(xs), ys ), + (IReadOnlyList xs, IReadOnlyList ys) => createArray4( xs , b2i(ys)), + (IReadOnlyList xs, IReadOnlyList ys) => createArray4( xs , s2i(ys)), + (IReadOnlyList xs, IReadOnlyList ys) => createArray4( xs , ys ), + + _ => throw new Exception( + $"Unexpected part indices types {first?.GetType().FullName ?? "null"} and {second?.GetType().FullName ?? "null"}. " + + $"Error 2f0672f5-8c6b-400b-8172-e83a30d70c28" + ) + }; + + object createArray1(uint first, int firstCount, uint second, int secondCount) + { + var count = firstCount + secondCount; + return Math.Max(first, second) switch + { + uint max when max <= byte .MaxValue => create((byte )first, (byte )second), + uint max when max <= short.MaxValue => create((short)first, (short)second), + uint max when max <= int .MaxValue => create((int )first, (int )second), + _ => throw new Exception("Invariant 129edb1c-066d-4ff2-8edf-8c5a67191dea.") + }; + + object create(T x0, T x1) where T : unmanaged + { + var xs = new T[count]; + for (var i = 0; i < firstCount; i++) xs[i] = x0; + for (var i = firstCount; i < count; i++) xs[i] = x1; + return xs; + } + } + + object createArray2(T first, int firstCount, IReadOnlyList second) where T : unmanaged + { + var count = firstCount + second.Count; + var xs = new T[count]; + int j = 0; + for (var i = 0; i < firstCount ; i++) xs[j++] = first; + for (var i = 0; i < second.Count; i++) xs[j++] = second[i]; + return xs; + } + + object createArray3(IReadOnlyList first, T second, int secondCount) where T : unmanaged + { + var count = first.Count + secondCount; + var xs = new T[count]; + int j = 0; + for (var i = 0; i < first.Count; i++) xs[j++] = first[i]; + for (var i = 0; i < secondCount; i++) xs[j++] = second; + return xs; + } + + object createArray4(IReadOnlyList first, IReadOnlyList second) where T : unmanaged + { + var count = first.Count + second.Count; + var xs = new T[count]; + int j = 0; + for (var i = 0; i < first .Count; i++) xs[j++] = first[i]; + for (var i = 0; i < second.Count; i++) xs[j++] = second[i]; + return xs; + } + + short[] b2s(IReadOnlyList xs) { var ys = new short[xs.Count]; for (var i = 0; i < xs.Count; i++) ys[i] = xs[i]; return ys; } + int [] b2i(IReadOnlyList xs) { var ys = new int [xs.Count]; for (var i = 0; i < xs.Count; i++) ys[i] = xs[i]; return ys; } + int [] s2i(IReadOnlyList xs) { var ys = new int [xs.Count]; for (var i = 0; i < xs.Count; i++) ys[i] = xs[i]; return ys; } + } + } + + /// + /// Merges part indices (null, uint, [byte|short|int] array). + /// + public static object? MergeIndices(IEnumerable<(object? indices, int count)> xs) + { + var (resultIndices, resultCount) = xs.FirstOrDefault(); + foreach (var (xIndices, xCount) in xs.Skip(1)) + { + MergeIndices(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, - (object x, null) => x, - (null, object y) => y, + (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, 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 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) => (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)), + //(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} and {second.GetType().FullName}. " + + $"Unexpected part indices types {first?.GetType().FullName ?? "null"} and {second?.GetType().FullName ?? "null"}. " + $"Error 2f0672f5-8c6b-400b-8172-e83a30d70c28" ) }; @@ -72,38 +200,38 @@ public static class PartIndexUtils } /// - /// Returns union of part indices (uint, IList of [byte|short|int]). + /// Merges multiple part index ranges (uint, IList of [byte|short|int]). /// - public static object? Union(params object?[] xs) + 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 = Union(result, xs[i]); + for (var i = 1; i < xs.Length; i++) result = MergeRanges(result, xs[i]); return result; } /// - /// Returns union of part indices (uint, IList of [byte|short|int]). + /// Merges multiple part index ranges (uint, IList of [byte|short|int]). /// - public static object? Union(IEnumerable xs) + public static object? MergeRanges(IEnumerable xs) { var result = xs.FirstOrDefault(); - foreach (var x in xs.Skip(1)) result = Union(result, x); + foreach (var x in xs.Skip(1)) result = MergeRanges(result, x); return result; } - public static Range1i ExtendedBy(in Range1i range, object? partIndices) + public static Range1i ExtendRangeBy(in Range1i range, object? partIndices) { checked { return partIndices switch { - null => range, - uint x => range.ExtendedBy((int)x), - IList xs => range.ExtendedBy((Range1i)new Range1b(xs)), + null => range, + uint x => range.ExtendedBy((int)x), + IList xs => range.ExtendedBy((Range1i)new Range1b(xs)), IList xs => range.ExtendedBy((Range1i)new Range1s(xs)), - IList xs => range.ExtendedBy(new Range1i(xs)), + IList xs => range.ExtendedBy(new Range1i(xs)), _ => throw new Exception( $"Unexpected part indices type {partIndices.GetType().FullName}. " + @@ -149,11 +277,11 @@ public static Range1i ExtendedBy(in Range1i range, object? partIndices) internal static object? Subset(object? partIndices, IReadOnlyList subsetIndices) => partIndices switch { - null => null, - uint x => x, - IList xs => subsetIndices.MapToArray(i => xs[i]), + null => null, + uint x => x, + IList xs => subsetIndices.MapToArray(i => xs[i]), IList xs => subsetIndices.MapToArray(i => xs[i]), - IList xs => subsetIndices.MapToArray(i => xs[i]), + IList xs => subsetIndices.MapToArray(i => xs[i]), _ => throw new Exception( $"Unexpected part indices type {partIndices.GetType().FullName}. " + diff --git a/src/Aardvark.Geometry.PointSet/Import/ImportChunks.cs b/src/Aardvark.Geometry.PointSet/Import/ImportChunks.cs index 6a8a6a0..d782689 100644 --- a/src/Aardvark.Geometry.PointSet/Import/ImportChunks.cs +++ b/src/Aardvark.Geometry.PointSet/Import/ImportChunks.cs @@ -80,7 +80,7 @@ public static PointSet Chunks(IEnumerable chunks, ImportConfig config) { if (chunk.HasPartIndices) { - partIndicesRange = PartIndexUtils.ExtendedBy(partIndicesRange, chunk.PartIndices); + partIndicesRange = PartIndexUtils.ExtendRangeBy(partIndicesRange, chunk.PartIndices); } if (config.Verbose) diff --git a/src/Aardvark.Geometry.PointSet/Octrees/Merge.cs b/src/Aardvark.Geometry.PointSet/Octrees/Merge.cs index 6033ad9..a426ab9 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/Merge.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/Merge.cs @@ -549,7 +549,7 @@ static int octant(Cell x) { result2 = a.WithSubNodes(subcells!); result2 = InjectPointsIntoTree( - a.PositionsAbsolute, a.Colors?.Value, a.Normals?.Value, a.Intensities?.Value, a.Classifications?.Value, + a.PositionsAbsolute, a.Colors?.Value, a.Normals?.Value, a.Intensities?.Value, a.Classifications?.Value, a.PartIndices, result2, result2.Cell, config); } else @@ -802,9 +802,9 @@ 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 chunk = new Chunk(ps, cs, ns, js, ks, partIndices: null /* TODO */, cell.BoundingBox); - throw new NotImplementedException("PARTINDICES"); + var chunk = new Chunk(ps, cs, ns, js, ks, partIndices: qs, cell.BoundingBox); if (config.NormalizePointDensityGlobal) { chunk = chunk.ImmutableFilterMinDistByCell(cell, config.ParseConfig); @@ -822,7 +822,7 @@ private static IPointCloudNode MergeLeafAndTreeWithIdenticalRootCell(IPointCloud if (a.IsLeaf == false || b.IsLeaf == true) throw new InvalidOperationException(); if (a.Cell != b.Cell) throw new InvalidOperationException(); - var result = InjectPointsIntoTree(a.PositionsAbsolute, a.Colors?.Value, a.Normals?.Value, a.Intensities?.Value, a.Classifications?.Value, b, a.Cell, config); + var result = InjectPointsIntoTree(a.PositionsAbsolute, a.Colors?.Value, a.Normals?.Value, a.Intensities?.Value, a.Classifications?.Value, a.PartIndices, b, a.Cell, config); //if (a.PointCountTree + b.PointCountTree != result.PointCountTree) throw new InvalidOperationException("Invariant db336387-4d1a-42fd-a582-48e8cac50fba."); if (a.Cell != result.Cell) throw new InvalidOperationException("Invariant 55551919-1a11-4ea9-bb4e-6f1a6b15e3d5."); return result; @@ -899,7 +899,7 @@ ImportConfig config } private static IPointCloudNode InjectPointsIntoTree( - IList psAbsolute, IList? cs, IList? ns, IList? js, IList? ks, + IList psAbsolute, IList? cs, IList? ns, IList? js, IList? ks, object? qs, IPointCloudNode a, Cell cell, ImportConfig config ) { @@ -909,8 +909,7 @@ private static IPointCloudNode InjectPointsIntoTree( if (a == null) { - var chunk = new Chunk(psAbsolute, cs, ns, js, ks, partIndices: null /* TODO */, cell.BoundingBox); - throw new NotImplementedException("PARTINDICES"); + var chunk = new Chunk(psAbsolute, cs, ns, js, ks, qs, cell.BoundingBox); if (config.NormalizePointDensityGlobal) { chunk = chunk.ImmutableFilterMinDistByCell(cell, config.ParseConfig); @@ -955,6 +954,7 @@ private static IPointCloudNode InjectPointsIntoTree( var nss = ns != null ? new List[8] : null; var iss = js != null ? new List[8] : null; var kss = ks != null ? new List[8] : null; + var qss = qs != null ? new object?[8] : null; #if DEBUG var bb = cell.BoundingBox; @@ -988,7 +988,8 @@ private static IPointCloudNode InjectPointsIntoTree( if (pss[j] != null) { if (x == null) throw new InvalidOperationException("Invariant 6afc7ca3-30da-4cb5-9a02-7572085e89bb."); - subcells[j] = InjectPointsIntoTree(pss[j], css?[j], nss?[j], iss?[j], kss?[j], x, cell.GetOctant(j), config); + throw new NotImplementedException("PARTINDICES"); + subcells[j] = InjectPointsIntoTree(pss[j], css?[j], nss?[j], iss?[j], kss?[j], qs: null /* TODO */, x, cell.GetOctant(j), config); } else {