diff --git a/src/Aardvark.Algodat.Tests/Program.cs b/src/Aardvark.Algodat.Tests/Program.cs
index 319bd8f0..2a131b9e 100644
--- a/src/Aardvark.Algodat.Tests/Program.cs
+++ b/src/Aardvark.Algodat.Tests/Program.cs
@@ -1751,7 +1751,7 @@ internal static void Test_Import_Regression()
//var filenames = new[]
//{
// @"W:\Datasets\Vgm\Data\E57\JBs_Haus.e57",
- // @"W:\Datasets\pointclouds\tests\JB_Haus_2022_KG.e57"
+ // //@"W:\Datasets\pointclouds\tests\JB_Haus_2022_KG.e57"
//};
var filenames = Directory.GetFiles(@"W:\Datasets\pointclouds\tests");
@@ -1775,7 +1775,7 @@ internal static void Test_Import_Regression()
var key = Path.GetFileName(filename);
- var storePath = $@"W:\aardvark\stores\{key}";
+ var storePath = $@"W:\aardvark\stores_noparts\{key}";
Directory.CreateDirectory(storePath);
using var storeRaw = new SimpleDiskStore(Path.Combine(storePath, "data.uds"));
var store = storeRaw.ToPointCloudStore();
@@ -1796,7 +1796,7 @@ internal static void Test_Import_Regression()
.WithMinDist(0)
.WithNormalizePointDensityGlobal(false)
//.WithProgressCallback(p => { Report.Line($"{p:0.00}"); })
- .WithEnabledPartIndices(true)
+ .WithEnabledPartIndices(false)
;
var pcl = PointCloud
@@ -2685,12 +2685,17 @@ static Task Parts_Test_20231006()
using var store = new SimpleDiskStore(Path.Combine(STOREPATH, "data.uds")).ToPointCloudStore();
var key = File.ReadAllText(Path.Combine(STOREPATH, "key.txt"));
var ps = store.GetPointSet(key);
+ var root = ps.Root.Value;
return Task.CompletedTask;
}
public static async Task Main(string[] _)
{
+ await Task.Delay(0); // avoid warnings if main contains no await
+
+
+
//await Parts_Test_20231006();
Test_Import_Regression();
diff --git a/src/Aardvark.Algodat.Tests/ViewsTests/ViewsFilterTests.cs b/src/Aardvark.Algodat.Tests/ViewsTests/ViewsFilterTests.cs
index 67c65137..67cd9229 100644
--- a/src/Aardvark.Algodat.Tests/ViewsTests/ViewsFilterTests.cs
+++ b/src/Aardvark.Algodat.Tests/ViewsTests/ViewsFilterTests.cs
@@ -55,6 +55,7 @@ private static IPointCloudNode CreateNode(Storage storage, V3f[] psGlobal, int[]
.Add(Durable.Octree.PointCountTreeLeafs, psLocal.LongLength)
.Add(Durable.Octree.PositionsLocal3fReference, psLocalId)
.Add(Durable.Octree.PointRkdTreeFDataReference, kdLocalId)
+ .Add(Durable.Octree.PerCellPartIndex1ui, 42u)
;
if (intensities != null)
@@ -68,6 +69,19 @@ private static IPointCloudNode CreateNode(Storage storage, V3f[] psGlobal, int[]
return result;
}
+ private static void CheckPartIndices(IPointCloudNode n)
+ {
+ if (n.TryGetPartIndices(out var qs))
+ {
+ Assert.True(qs.Length == n.PointCountCell);
+ Assert.True(qs.All(q => q == 42));
+ }
+ else
+ {
+ Assert.Fail("Node has no part indices.");
+ }
+ }
+
#region FilterInsideBox3d
[Test]
@@ -80,6 +94,8 @@ public void FilterInsideBox3d_AllInside()
Assert.IsTrue(f.HasPositions);
var ps = f.PositionsAbsolute;
Assert.IsTrue(ps.Length == 100);
+
+ CheckPartIndices(f);
}
[Test]
@@ -90,6 +106,8 @@ public void FilterInsideBox3d_AllOutside()
var f = FilteredNode.Create(a, new FilterInsideBox3d(a.BoundingBoxExactGlobal + V3d.IOO));
Assert.IsTrue(f.PointCountCell == 0);
+
+ CheckPartIndices(f);
}
[Test]
@@ -128,6 +146,8 @@ public void FilterInsideBox3d_Partial()
var ps = f.PositionsAbsolute;
var count = ps.Count(p => p.Z <= 0.5);
Assert.IsTrue(ps.Length == count);
+
+ CheckPartIndices(f);
}
#endregion
@@ -144,6 +164,8 @@ public void FilterOutsideBox3d_AllInside()
Assert.IsTrue(f.HasPositions);
var ps = f.PositionsAbsolute;
Assert.IsTrue(ps.Length == 100);
+
+ CheckPartIndices(f);
}
[Test]
@@ -154,6 +176,8 @@ public void FilterOutsideBox3d_AllOutside()
var f = FilteredNode.Create(a, new FilterOutsideBox3d(a.BoundingBoxExactGlobal));
Assert.IsTrue(f.PointCountCell == 0);
+
+ CheckPartIndices(f);
}
[Test]
@@ -167,6 +191,8 @@ public void FilterOutsideBox3d_Partial()
var ps = f.PositionsAbsolute;
var count = ps.Count(p => p.Z <= 0.5);
Assert.IsTrue(ps.Length == count);
+
+ CheckPartIndices(f);
}
#endregion
@@ -184,6 +210,8 @@ public void FilterIntensity_AllInside()
Assert.IsTrue(f.HasIntensities);
var js = f.Intensities.Value;
Assert.IsTrue(js.Length == 100);
+
+ CheckPartIndices(f);
}
[Test]
@@ -194,6 +222,8 @@ public void FilterIntensity_AllOutside()
var f = FilteredNode.Create(a, new FilterIntensity(new Range1i(-30000, -10000)));
Assert.IsTrue(f.PointCountCell == 0);
+
+ CheckPartIndices(f);
}
[Test]
@@ -211,6 +241,8 @@ public void FilterIntensity_Partial()
Assert.IsTrue(js.Length == 2);
Assert.IsTrue(js[0] == 10000);
Assert.IsTrue(js[1] == 20000);
+
+ CheckPartIndices(f);
}
#endregion
@@ -328,10 +360,14 @@ public void EncodeDecodeRoundtrip()
var buffer = ((IPointCloudNode)f).Encode();
Assert.IsTrue(buffer != null);
+ CheckPartIndices(f);
+
var g = FilteredNode.Decode(storage, buffer);
Assert.IsTrue(f.Id == g.Id);
Assert.IsTrue(f.Node.Id == g.Node.Id);
+ CheckPartIndices(g);
+
var fFilterJson = f.Filter.Serialize().ToString();
var gFilterJson = g.Filter.Serialize().ToString();
Assert.IsTrue(fFilterJson == gFilterJson);
@@ -366,6 +402,8 @@ public void CanDeletePoints()
Assert.IsTrue(!b.QueryAllPoints().SelectMany(chunk => chunk.Positions).Any(p => q1.Contains(p)));
Assert.IsTrue(b.HasCentroidLocal);
+
+ CheckPartIndices(f);
}
#endregion
diff --git a/src/Aardvark.Data.E57/ASTM_E57.cs b/src/Aardvark.Data.E57/ASTM_E57.cs
index 4ef1d20b..3166bf3c 100644
--- a/src/Aardvark.Data.E57/ASTM_E57.cs
+++ b/src/Aardvark.Data.E57/ASTM_E57.cs
@@ -44,7 +44,7 @@ public static class ASTM_E57
///
/// Physical E57 file offset.
///
- public struct E57PhysicalOffset
+ public readonly struct E57PhysicalOffset
{
public readonly long Value;
public E57PhysicalOffset(long value) => Value
@@ -59,7 +59,7 @@ public E57PhysicalOffset(long value) => Value
///
/// Logical E57 file offset.
///
- public struct E57LogicalOffset
+ public readonly struct E57LogicalOffset
{
public readonly long Value;
public E57LogicalOffset(long value) { Value = value; }
@@ -2263,7 +2263,7 @@ public struct E57DataPacketHeader
///
internal ushort ByteStreamCount;
- internal bool CompressorRestart => (PacketFlags & 0b00000001) != 0;
+ internal readonly bool CompressorRestart => (PacketFlags & 0b00000001) != 0;
internal static E57DataPacketHeader Parse(byte[] buffer) => new()
{
diff --git a/src/Aardvark.Geometry.PointSet/Octrees/IPointCloudNode.cs b/src/Aardvark.Geometry.PointSet/Octrees/IPointCloudNode.cs
index 30e254ef..40338c41 100644
--- a/src/Aardvark.Geometry.PointSet/Octrees/IPointCloudNode.cs
+++ b/src/Aardvark.Geometry.PointSet/Octrees/IPointCloudNode.cs
@@ -244,7 +244,9 @@ public interface IPointCloudNode
#region PartIndices
- ///
+ ///
+ /// True if this node has part indices.
+ ///
bool HasPartIndices { get; }
///
@@ -252,6 +254,12 @@ public interface IPointCloudNode
///
object? PartIndices { get; }
+ ///
+ /// Get per-point part indices as an int array (regardless of internal representation).
+ /// Returns false if node has no part indices.
+ ///
+ bool TryGetPartIndices([NotNullWhen(true)] out int[]? result);
+
#endregion
#region Velocities
diff --git a/src/Aardvark.Geometry.PointSet/Octrees/Lod.cs b/src/Aardvark.Geometry.PointSet/Octrees/Lod.cs
index a318326a..78ca5dee 100644
--- a/src/Aardvark.Geometry.PointSet/Octrees/Lod.cs
+++ b/src/Aardvark.Geometry.PointSet/Octrees/Lod.cs
@@ -195,6 +195,7 @@ internal static Array AggregateSubArrays(int[] counts, int splitLimit, object[]
if (xss.All(xs => xs == null || xs is uint))
{
var perCellIndices = xss.Where(xs => xs != null).Select(xs => (uint)xs!).ToArray();
+ if (perCellIndices.Length == 0) return null;
var allIdentical = true;
for (var i = 1; i < perCellIndices.Length; i++) if (perCellIndices[i] != perCellIndices[0]) { allIdentical = false; break; }
if (allIdentical) return perCellIndices[0];
diff --git a/src/Aardvark.Geometry.PointSet/Octrees/PointSetNode.cs b/src/Aardvark.Geometry.PointSet/Octrees/PointSetNode.cs
index b45cabd8..f1a4e0f6 100644
--- a/src/Aardvark.Geometry.PointSet/Octrees/PointSetNode.cs
+++ b/src/Aardvark.Geometry.PointSet/Octrees/PointSetNode.cs
@@ -506,16 +506,16 @@ public PointSetNode WriteToStore()
#endregion
-#region Properties (state to serialize)
+ #region Properties (state to serialize)
///
/// Durable properties.
///
private ImmutableDictionary Data { get; } = ImmutableDictionary.Empty;
-#endregion
+ #endregion
-#region Properties (derived/runtime, non-serialized)
+ #region Properties (derived/runtime, non-serialized)
///
/// Runtime.
@@ -529,7 +529,7 @@ public PointSetNode WriteToStore()
[JsonIgnore]
private Dictionary PersistentRefs { get; } = new Dictionary();
-#region Cell attributes
+ #region Cell attributes
///
/// This node's unique id (16 bytes).
@@ -582,9 +582,9 @@ public Guid?[]? SubnodeIds
}
}
-#endregion
+ #endregion
-#region Positions
+ #region Positions
///
[JsonIgnore]
@@ -681,9 +681,9 @@ public PersistentRef Positions
[JsonIgnore]
public V3d[] PositionsAbsolute => Positions?.Value.Map(p => new V3d(Center.X + p.X, Center.Y + p.Y, Center.Z + p.Z))!;
-#endregion
+ #endregion
-#region BoundingBoxExactLocal
+ #region BoundingBoxExactLocal
///
[JsonIgnore]
@@ -693,9 +693,9 @@ public PersistentRef Positions
[JsonIgnore]
public Box3f BoundingBoxExactLocal => Data.TryGetValue(Durable.Octree.BoundingBoxExactLocal, out var o) ? (Box3f)o : default;
-#endregion
+ #endregion
-#region BoundingBoxExactGlobal
+ #region BoundingBoxExactGlobal
///
[JsonIgnore]
@@ -705,9 +705,9 @@ public PersistentRef Positions
[JsonIgnore]
public Box3d BoundingBoxExactGlobal => Data.TryGetValue(Durable.Octree.BoundingBoxExactGlobal, out var o) ? (Box3d)o : default;
-#endregion
+ #endregion
-#region Colors
+ #region Colors
///
[JsonIgnore]
@@ -778,9 +778,9 @@ public PersistentRef? Colors
}
}
-#endregion
+ #endregion
-#region Normals
+ #region Normals
///
[JsonIgnore]
@@ -817,9 +817,9 @@ public PersistentRef? Normals
}
}
-#endregion
+ #endregion
-#region Intensities
+ #region Intensities
///
[JsonIgnore]
@@ -856,9 +856,9 @@ public PersistentRef? Intensities
}
}
-#endregion
+ #endregion
-#region Classifications
+ #region Classifications
///
[JsonIgnore]
@@ -899,9 +899,11 @@ public PersistentRef? Classifications
#region PartIndices
- ///
- [JsonIgnore]
+ ///
+ /// True if this node has part indices.
+ ///
+ [JsonIgnore]
[MemberNotNullWhen(true, nameof(PartIndices))]
public bool HasPartIndices =>
Data.ContainsKey(Durable.Octree.PerCellPartIndex1ui) ||
@@ -926,6 +928,26 @@ public object? PartIndices
}
}
+ ///
+ /// Get per-point part indices as an int array (regardless of internal representation).
+ /// Returns false if node has no part indices.
+ ///
+ public bool TryGetPartIndices([NotNullWhen(true)] out int[]? result)
+ {
+ switch (PartIndices)
+ {
+ case null: result = null; return false;
+ case uint x: checked { result = new int[PointCountCell].Set((int)x); return true; }
+ case byte[] xs: result = xs.Map(x => (int)x); return true;
+ case short[] xs: result = xs.Map(x => (int)x); return true;
+ case int[] xs: result = xs; return true;
+ default: throw new Exception(
+ $"Unexpected type {PartIndices.GetType().FullName}. " +
+ $"Error 278c88f6-d504-4a17-9752-8cca614505f1."
+ );
+ }
+ }
+
#endregion
#region Velocities
@@ -948,7 +970,7 @@ public object? PartIndices
#endregion
-#region KdTree
+ #region KdTree
///
[JsonIgnore]
@@ -975,9 +997,9 @@ public object? PartIndices
: null
;
-#endregion
+ #endregion
-#region CentroidLocal
+ #region CentroidLocal
///
public bool HasCentroidLocal => PointCountCell > 0 || Data.ContainsKey(Durable.Octree.PositionsLocal3fCentroid);
@@ -1048,9 +1070,9 @@ public float CentroidLocalStdDev
}
}
-#endregion
+ #endregion
-#region TreeDepth
+ #region TreeDepth
///
public bool HasMinTreeDepth => Data.ContainsKey(Durable.Octree.MinTreeDepth);
@@ -1064,9 +1086,9 @@ public float CentroidLocalStdDev
///
public int MaxTreeDepth => (int)Data.Get(Durable.Octree.MaxTreeDepth);
-#endregion
+ #endregion
-#region PointDistance
+ #region PointDistance
///
public bool HasPointDistanceAverage => Data.ContainsKey(Durable.Octree.AveragePointDistance);
@@ -1080,7 +1102,7 @@ public float CentroidLocalStdDev
///
public float PointDistanceStandardDeviation => (Data.TryGetValue(Durable.Octree.AveragePointDistanceStdDev, out var value) && value is float x) ? x : -1.0f;
-#endregion
+ #endregion
///
/// Subnodes (8), or null if leaf.
@@ -1230,7 +1252,7 @@ private static Guid ComputeAndStoreKdTree(Storage storage, V3f[] ps)
#endregion
-#region Durable codec
+ #region Durable codec
///
///
@@ -1246,9 +1268,9 @@ public static PointSetNode Decode(Storage storage, byte[] buffer)
return new PointSetNode(data, storage, false);
}
-#endregion
+ #endregion
-#region IPointCloudNode
+ #region IPointCloudNode
Guid IPointCloudNode.Id => Id;
@@ -1305,6 +1327,6 @@ public Box3d BoundingBoxApproximate
IPointCloudNode IPointCloudNode.With(IReadOnlyDictionary replacements) => With(replacements);
-#endregion
+ #endregion
}
}
diff --git a/src/Aardvark.Geometry.PointSet/Views/FilteredNode.cs b/src/Aardvark.Geometry.PointSet/Views/FilteredNode.cs
index e3fc6cf5..12cc22a2 100644
--- a/src/Aardvark.Geometry.PointSet/Views/FilteredNode.cs
+++ b/src/Aardvark.Geometry.PointSet/Views/FilteredNode.cs
@@ -438,14 +438,38 @@ public PersistentRef> KdTree
#region PartIndices
- ///
+ ///
+ /// True if this node has part indices.
+ ///
[MemberNotNullWhen(true, nameof(PartIndices))]
public bool HasPartIndices => Node.HasPartIndices;
///
/// Octree. Per-point or per-cell part indices.
///
- public object? PartIndices => Node.PartIndices;
+ public object? PartIndices => PartIndexUtils.Subset(Node.PartIndices, null!);
+
+ ///
+ /// Get per-point part indices as an int array (regardless of internal representation).
+ /// Returns false if node has no part indices.
+ ///
+ public bool TryGetPartIndices([NotNullWhen(true)] out int[]? result)
+ {
+ var qs = SubsetIndexArray != null ? PartIndexUtils.Subset(PartIndices, SubsetIndexArray) : PartIndices;
+ switch (qs)
+ {
+ case null: result = null; return false;
+ case uint x: checked { result = new int[PointCountCell].Set((int)x); return true; }
+ case byte[] xs: result = xs.Map(x => (int)x); return true;
+ case short[] xs: result = xs.Map(x => (int)x); return true;
+ case int[] xs: result = xs; return true;
+ default:
+ throw new Exception(
+ $"Unexpected type {qs.GetType().FullName}. " +
+ $"Error ccc0b898-fe4f-4373-ac15-42da763fe5ab."
+ );
+ }
+ }
#endregion
@@ -521,6 +545,7 @@ public PersistentRef> KdTree
#endregion
private readonly Dictionary m_cache = new();
+
private PersistentRef? GetSubArray(Def def, PersistentRef? originalValue)
{
if (m_cache.TryGetValue(def.Id, out var o) && o is PersistentRef x) return x;
@@ -536,6 +561,21 @@ public PersistentRef> KdTree
return result;
}
+ private int[]? _subsetIndexArray = null;
+ private int[]? SubsetIndexArray
+ {
+ get
+ {
+ if (_subsetIndexArray != null) return _subsetIndexArray;
+ if (m_activePoints == null) return null;
+
+ var imax = PointCountCell;
+ var xs = new List();
+ for (var i = 0; i < imax; i++) if (m_activePoints.Contains(i)) xs.Add(i);
+ return _subsetIndexArray = xs.ToArray();
+ }
+ }
+
#endregion
#region Not supported ...