diff --git a/src/Aardvark.Algodat.Tests/PointSetTests.cs b/src/Aardvark.Algodat.Tests/PointSetTests.cs index 79a130a0..ffa86055 100644 --- a/src/Aardvark.Algodat.Tests/PointSetTests.cs +++ b/src/Aardvark.Algodat.Tests/PointSetTests.cs @@ -27,14 +27,14 @@ public class PointSetTests internal static Storage CreateStorage() { var x = new SimpleMemoryStore(); - Action> add = (name, value, create) => x.Add(name, create()); + void add(string name, object value, Func create) => x.Add(name, create()); return new Storage(add, x.Get, x.GetSlice, x.Remove, x.Dispose, x.Flush, cache: default); } internal static Storage CreateDiskStorage(string dbDiskLocation) { var x = new SimpleDiskStore(dbDiskLocation); - Action> add = (name, value, create) => x.Add(name, create()); + void add(string name, object value, Func create) => x.Add(name, create()); return new Storage(add, x.Get, x.GetSlice, x.Remove, x.Dispose, x.Flush, cache: default); } @@ -56,7 +56,7 @@ public void CanCreateEmptyPointSet() public void CanCreatePointSetFromSinglePoint() { var store = CreateStorage(); - var ps = new List { new V3d(0.1, 0.2, 0.3) }; + var ps = new List { new(0.1, 0.2, 0.3) }; var cs = new List { C4b.White }; var pointset = PointSet.Create( store, "id", ps, cs, null, null, null, 1000, @@ -72,7 +72,7 @@ public void CanCreatePointSetFromSinglePoint() [Test] public void CanCreateInMemoryPointSet() { - var ps = new List { new V3d(0.5, 0.5, 0.5) }; + var ps = new List { new(0.5, 0.5, 0.5) }; var cs = new List { C4b.White }; var ns = new List { V3f.ZAxis }; var js = new List { 123 }; @@ -83,7 +83,7 @@ public void CanCreateInMemoryPointSet() [Test] public void CanCreateInMemoryPointSetWithoutColors() { - var ps = new List { new V3d(0.5, 0.5, 0.5) }; + var ps = new List { new(0.5, 0.5, 0.5) }; var ns = new List { V3f.ZAxis }; var js = new List { 123 }; var ks = new List { 42 }; @@ -93,7 +93,7 @@ public void CanCreateInMemoryPointSetWithoutColors() [Test] public void CanCreateInMemoryPointSetWithoutNormals() { - var ps = new List { new V3d(0.5, 0.5, 0.5) }; + var ps = new List { new(0.5, 0.5, 0.5) }; var cs = new List { C4b.White }; var js = new List { 123 }; var ks = new List { 42 }; @@ -103,7 +103,7 @@ public void CanCreateInMemoryPointSetWithoutNormals() [Test] public void CanCreateInMemoryPointSetWithoutIntensities() { - var ps = new List { new V3d(0.5, 0.5, 0.5) }; + var ps = new List { new(0.5, 0.5, 0.5) }; var cs = new List { C4b.White }; var ns = new List { V3f.ZAxis }; var ks = new List { 42 }; @@ -113,7 +113,7 @@ public void CanCreateInMemoryPointSetWithoutIntensities() [Test] public void CanCreateInMemoryPointSetWithoutClassifications() { - var ps = new List { new V3d(0.5, 0.5, 0.5) }; + var ps = new List { new(0.5, 0.5, 0.5) }; var cs = new List { C4b.White }; var ns = new List { V3f.ZAxis }; var js = new List { 123 }; @@ -160,7 +160,7 @@ public void PointSetAttributes_EmptyPointSet() [Test] public void PointSetAttributes_All() { - var ps = new List { new V3d(0.5, 0.5, 0.5) }; + var ps = new List { new(0.5, 0.5, 0.5) }; var cs = new List { C4b.White }; var ns = new List { V3f.ZAxis }; var js = new List { 123 }; @@ -179,7 +179,7 @@ public void PointSetAttributes_All() [Test] public void PointSetAttributes_NoLod() { - var ps = new List { new V3d(0.5, 0.5, 0.5) }; + var ps = new List { new(0.5, 0.5, 0.5) }; var cs = new List { C4b.White }; var ns = new List { V3f.ZAxis }; var js = new List { 123 }; @@ -198,7 +198,7 @@ public void PointSetAttributes_NoLod() [Test] public void PointSetAttributes_PositionsAndColors() { - var ps = new List { new V3d(0.5, 0.5, 0.5) }; + var ps = new List { new(0.5, 0.5, 0.5) }; var cs = new List { C4b.White }; var storage = PointCloud.CreateInMemoryStore(cache: default); var pointset = PointSet.Create(storage, "test", ps, cs, null, null, null, 1, generateLod: true, isTemporaryImportNode: true, default); @@ -208,5 +208,70 @@ public void PointSetAttributes_PositionsAndColors() Assert.IsTrue(pointset.HasNormals == true); Assert.IsTrue(pointset.HasPositions == true); } + + [Test] + public void PointSet_PartIndexRange_No() + { + var ps = new List { new(0.5, 0.5, 0.5) }; + var storage = PointCloud.CreateInMemoryStore(cache: default); + var pointset = PointSet.Create(storage, "test", ps, null, null, null, null, 1, generateLod: false, isTemporaryImportNode: true, default); + Assert.IsTrue(pointset.PartIndexRange == Range1i.Invalid); + Assert.IsTrue(pointset.HasPartIndexRange == false); + } + + [Test] + public void PointSet_PartIndexRange() + { + var ps = new List { new(0.5, 0.5, 0.5) }; + var storage = PointCloud.CreateInMemoryStore(cache: default); + var pointset = PointSet.Create( + storage, "test", ps, null, null, null, null, 1, generateLod: false, isTemporaryImportNode: true + ) + .WithPartIndexRange(new(7, 11)) + ; + Assert.IsTrue(pointset.PartIndexRange == new Range1i(7, 11)); + Assert.IsTrue(pointset.HasPartIndexRange == true); + } + + [Test] + public void PointSet_PartIndexRange_Serialization() + { + var ps = new List { new(0.5, 0.5, 0.5) }; + var storage = PointCloud.CreateInMemoryStore(cache: default); + + var pointset = PointSet.Create( + storage, "test", ps, null, null, null, null, 1, generateLod: false, isTemporaryImportNode: true + ) + .WithPartIndexRange(new(7, 11)) + ; + + var json = pointset.ToJson(); + var reloaded = PointSet.Parse(json, storage); + + Assert.IsTrue(pointset.Id == reloaded.Id); + Assert.IsTrue(pointset.SplitLimit == reloaded.SplitLimit); + Assert.IsTrue(pointset.Root.Value.Id == reloaded.Root.Value.Id); + Assert.IsTrue(pointset.PartIndexRange == reloaded.PartIndexRange); + } + + [Test] + public void PointSet_PartIndexRange_Serialization_NoRange() + { + var ps = new List { new(0.5, 0.5, 0.5) }; + var storage = PointCloud.CreateInMemoryStore(cache: default); + + var pointset = PointSet.Create( + storage, "test", ps, null, null, null, null, 1, generateLod: false, isTemporaryImportNode: true + ); + + var json = pointset.ToJson(); + var reloaded = PointSet.Parse(json, storage); + + Assert.IsTrue(pointset.Id == reloaded.Id); + Assert.IsTrue(pointset.SplitLimit == reloaded.SplitLimit); + Assert.IsTrue(pointset.Root.Value.Id == reloaded.Root.Value.Id); + Assert.IsTrue(pointset.PartIndexRange == reloaded.PartIndexRange); + Assert.IsTrue(reloaded.PartIndexRange.IsInvalid); + } } } diff --git a/src/Aardvark.Geometry.PointSet/Octrees/PointSet.cs b/src/Aardvark.Geometry.PointSet/Octrees/PointSet.cs index f3c14f8f..4a5bb183 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/PointSet.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/PointSet.cs @@ -39,7 +39,7 @@ public class PointSet /// public static PointSet Create(Storage storage, string key, IList positions, IList colors, IList normals, IList intensities, IList classifications, - int octreeSplitLimit, bool generateLod, bool isTemporaryImportNode, CancellationToken ct + int octreeSplitLimit, bool generateLod, bool isTemporaryImportNode, CancellationToken ct = default ) { if (key == null) throw new ArgumentNullException(nameof(key)); @@ -108,21 +108,30 @@ public PointSet(Storage storage, string key) SplitLimit = 0; } + private PointSet() + { + } + #endregion #region Properties (state to serialize) /// /// - public string Id { get; } + public string Id { get; init; } /// /// - public int SplitLimit { get; } + public int SplitLimit { get; init; } /// /// - public PersistentRef Root { get; } + public PersistentRef Root { get; init; } + + /// + /// Range (inclusive) of part indices, or invalid range if no part indices are stored. + /// + public Range1i PartIndexRange { get; init; } = Range1i.Invalid; #endregion @@ -135,7 +144,8 @@ public JsonNode ToJson() => JsonSerializer.SerializeToNode(new Id, RootCellId = Root?.Id, OctreeId = Root?.Id, - SplitLimit + SplitLimit, + PartIndexRange }); /// @@ -156,8 +166,15 @@ public static PointSet Parse(JsonNode json, Storage storage) // id var id = (string)o["Id"]; + // part index range (JsonArray) + var partIndexRangeArray = (JsonArray)o["PartIndexRange"]; + var partIndexRange = partIndexRangeArray != null + ? new Range1i((int)partIndexRangeArray[0], (int)partIndexRangeArray[1]) + : Range1i.Invalid + ; + // - return new PointSet(storage, id, octree?.Value ?? PointSetNode.Empty, splitLimit); + return new PointSet(storage, id, octree?.Value ?? PointSetNode.Empty, splitLimit).WithPartIndexRange(partIndexRange); } #endregion @@ -167,7 +184,7 @@ public static PointSet Parse(JsonNode json, Storage storage) /// /// [JsonIgnore] - public readonly Storage Storage; + public Storage Storage { get; init; } /// /// Returns true if pointset is empty. @@ -220,6 +237,10 @@ public Box3d BoundingBox /// public bool HasPositions => Root != null && Root.Value.HasPositions; + + /// + public bool HasPartIndexRange => PartIndexRange.IsValid; + #endregion #region Immutable operations @@ -244,6 +265,8 @@ public PointSet Merge(PointSet other, Action pointsMergedCallback, ImportC } } + public PointSet WithPartIndexRange(Range1i x) => new() { Id = Id, PartIndexRange = x, Root = Root, SplitLimit = SplitLimit, Storage = Storage }; + #endregion } }