diff --git a/src/Aardvark.Algodat.Tests/DeleteTests.cs b/src/Aardvark.Algodat.Tests/DeleteTests.cs index f915c4e..19f17ce 100644 --- a/src/Aardvark.Algodat.Tests/DeleteTests.cs +++ b/src/Aardvark.Algodat.Tests/DeleteTests.cs @@ -130,12 +130,16 @@ public void DeleteDelete() { var q1 = new Box3d(new V3d(0.0), new V3d(0.1)); var a = CreateRandomPointsInUnitCube(50000, 1024); + + // 1. delete a subset of points var b = a.Delete(n => q1.Contains(n.BoundingBoxExactGlobal), n => !(q1.Contains(n.BoundingBoxExactGlobal) || q1.Intersects(n.BoundingBoxExactGlobal)), p => q1.Contains(p), a.Storage, CancellationToken.None); b.ValidateTree(); Assert.IsTrue(b.Root?.Value.NoPointIn(p => q1.Contains(p))); + + // 2. delete ALL remaining points var c = b.Delete(n => true, n => false, p => true, a.Storage, CancellationToken.None); - c.ValidateTree(); - Assert.IsTrue(c.PointCount == 0L); + // if all points are deleted, then 'Delete' returns null + Assert.Null(c); } } @@ -166,10 +170,9 @@ public void DeleteAll() { var a = CreateRegularPointsInUnitCube(10, 1); var b = a.Delete(n => true, n => false, p => true, a.Storage, CancellationToken.None); - b.ValidateTree(); - Assert.IsTrue(a.PointCount != b.PointCount); - Assert.IsTrue(b.PointCount == 0); - Assert.IsTrue(a.Id != b.Id); + + // if all points are deleted, then 'Delete' returns null + Assert.Null(b); } [Test] diff --git a/src/Aardvark.Algodat.Tests/InlinedNodeTests.cs b/src/Aardvark.Algodat.Tests/InlinedNodeTests.cs index eac89d0..2b453bd 100644 --- a/src/Aardvark.Algodat.Tests/InlinedNodeTests.cs +++ b/src/Aardvark.Algodat.Tests/InlinedNodeTests.cs @@ -54,7 +54,7 @@ 1 1 0 255 0 ; var pointset = PointCloud.Import(Data.Points.Import.Ply.Chunks(ms, 0, config.ParseConfig), config); - var pcl = pointset.Root.Value; + var _ /*pcl*/ = pointset.Root.Value; var inlineConfig = new InlineConfig(collapse: true, gzipped: true); var inlinedNodes = store.EnumerateOctreeInlined(key, inlineConfig); diff --git a/src/Aardvark.Algodat.Tests/ParsingTests.cs b/src/Aardvark.Algodat.Tests/ParsingTests.cs index c39a77f..d9eaca7 100644 --- a/src/Aardvark.Algodat.Tests/ParsingTests.cs +++ b/src/Aardvark.Algodat.Tests/ParsingTests.cs @@ -59,11 +59,11 @@ public void ChunkStreamAtNewlines_ThrowsIfMaxChunkSizeIsZeroOrNegative() [Test] public void ChunkStreamAtNewlines_EmptyStreamGivesEmptySequence() { - var buffer = new byte[0]; + var buffer = Array.Empty(); var ms = new MemoryStream(buffer); var xs = ms.ChunkStreamAtNewlines(10, 10, CancellationToken.None); - Assert.IsTrue(xs.Count() == 0); + Assert.IsTrue(!xs.Any()); } [Test] @@ -102,7 +102,7 @@ public void ParseBuffers_Works() var buffer = new byte[10] { 1, 1, 10, 2, 2, 10, 3, 3, 10, 4 }; var ms = new MemoryStream(buffer); - static Chunk parse(byte[] _, int __, double ___) => new Chunk(new[] { V3d.Zero }); + static Chunk parse(byte[] _, int __, double ___) => new(new[] { V3d.Zero }); var xs = ms .ChunkStreamAtNewlines(10, 5, CancellationToken.None) diff --git a/src/Aardvark.Algodat.Tests/PointSetNodeTests.cs b/src/Aardvark.Algodat.Tests/PointSetNodeTests.cs index d486b92..401a5ae 100644 --- a/src/Aardvark.Algodat.Tests/PointSetNodeTests.cs +++ b/src/Aardvark.Algodat.Tests/PointSetNodeTests.cs @@ -54,6 +54,10 @@ internal static PointSetNode CreateNode(Storage storage) var kd = ps.BuildKdTree(); storage.Add(kdId, kd.Data); + var csId = Guid.NewGuid(); + var cs = new[] { C4b.Yellow }; + storage.Add(csId, cs); + var data = EmptyData .Add(Durable.Octree.NodeId, id) .Add(Durable.Octree.Cell, cell) @@ -62,6 +66,7 @@ internal static PointSetNode CreateNode(Storage storage) .Add(Durable.Octree.PointRkdTreeFDataReference, kdId) .Add(Durable.Octree.BoundingBoxExactLocal, new Box3f(ps)) .Add(Durable.Octree.BoundingBoxExactGlobal, ((Box3d)new Box3f(ps)) + cell.GetCenter()) + .Add(Durable.Octree.Colors4bReference, csId) ; return new PointSetNode(data, storage, writeToStore: true); diff --git a/src/Aardvark.Algodat.Tests/PointSetTests.cs b/src/Aardvark.Algodat.Tests/PointSetTests.cs index 5225ecf..9b682e5 100644 --- a/src/Aardvark.Algodat.Tests/PointSetTests.cs +++ b/src/Aardvark.Algodat.Tests/PointSetTests.cs @@ -47,7 +47,6 @@ public void CanCreateEmptyPointSet() Assert.IsTrue(pointset.Id == "PointSet.Empty"); Assert.IsTrue(pointset.IsEmpty == true); Assert.IsTrue(pointset.PointCount == 0); - Assert.IsTrue(pointset.Root == null); Assert.IsTrue(pointset.SplitLimit == 0); pointset.ValidateTree(); } @@ -268,9 +267,9 @@ public void PointSet_PartIndexRange_Serialization() 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.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); } @@ -287,9 +286,9 @@ public void PointSet_PartIndexRange_Serialization_NoRange() 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.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.Algodat.Tests/TestUtils.cs b/src/Aardvark.Algodat.Tests/TestUtils.cs index 957acaa..faeed90 100644 --- a/src/Aardvark.Algodat.Tests/TestUtils.cs +++ b/src/Aardvark.Algodat.Tests/TestUtils.cs @@ -18,42 +18,52 @@ public static void ValidateTree(this IPointCloudNode node, int splitLimit, bool { if (node != null) { - Assert.IsTrue(node.PointCountTree > 0); + //Assert.IsTrue(node.PointCountTree > 0); Assert.IsTrue(node.HasBoundingBoxExactGlobal || node.HasBoundingBoxExactLocal); if (node.IsLeaf || hasLod) { - Assert.IsTrue(node.PointCountCell > 0); - Assert.IsTrue(node.HasPositions); - var ps = node.PositionsAbsolute; - var realBb = node.BoundingBoxExactGlobal; - var bb = realBb.EnlargedByRelativeEps(0.01); - Assert.IsTrue(ps.All(p => bb.Contains(p))); - Assert.IsTrue(ps.Length <= 1 || node.HasKdTree); + if (node.Id != Guid.Empty) + { + Assert.IsTrue(node.PointCountCell > 0); + Assert.IsTrue(node.HasPositions); + var ps = node.PositionsAbsolute; + var realBb = node.BoundingBoxExactGlobal; + var bb = realBb.EnlargedByRelativeEps(0.01); + Assert.IsTrue(ps.Length == 0 || ps.All(p => bb.Contains(p))); + Assert.IsTrue(ps.Length <= 1 || node.HasKdTree); + } - if (node.HasNormals) Assert.IsTrue(node.Normals.Value.Length == ps.Length); - if (node.HasColors) Assert.IsTrue(node.Colors.Value.Length == ps.Length); - if (node.HasIntensities) Assert.IsTrue(node.Intensities.Value.Length == ps.Length); - if (node.HasClassifications) Assert.IsTrue(node.Classifications.Value.Length == ps.Length); + if (node.HasNormals ) Assert.IsTrue(node.Normals .Value.Length == node.PointCountCell); + if (node.HasColors ) Assert.IsTrue(node.Colors .Value.Length == node.PointCountCell); + if (node.HasIntensities ) Assert.IsTrue(node.Intensities .Value.Length == node.PointCountCell); + if (node.HasClassifications) Assert.IsTrue(node.Classifications.Value.Length == node.PointCountCell); } if (node.IsLeaf) { - var ps = node.PositionsAbsolute; Assert.IsTrue(node.PointCountCell == node.PointCountTree); - Assert.IsTrue(ps.Length == node.PointCountTree); - Assert.IsTrue(ps.Length <= splitLimit); + if (node.Id != Guid.Empty) + { + var ps = node.PositionsAbsolute; + Assert.IsTrue(ps.Length == node.PointCountTree); + Assert.IsTrue(ps.Length <= splitLimit); + } } else { - var realBb = node.BoundingBoxExactGlobal; - var bb = realBb.EnlargedByRelativeEps(0.01); - Assert.IsTrue(node.PointCountTree > splitLimit); var nodes = node.Subnodes.Map(n => n?.Value); - var nodeBB = new Box3d(nodes.Select(n => n != null ? n.BoundingBoxExactGlobal : Box3d.Invalid)); - Assert.IsTrue(realBb == nodeBB); + Assert.IsTrue(node.PointCountTree > splitLimit); Assert.IsTrue(node.PointCountTree == nodes.Sum(n => n != null ? n.PointCountTree : 0)); + if (node.Id != Guid.Empty) + { + var realBb = node.BoundingBoxExactGlobal; + var bb = realBb.EnlargedByRelativeEps(0.01); + var nodeBB = new Box3d(nodes.Select(n => n != null ? n.BoundingBoxExactGlobal : Box3d.Invalid)); + Assert.IsTrue(realBb == nodeBB); + } + foreach (var n in nodes) { ValidateTree(n, splitLimit, hasLod); diff --git a/src/Aardvark.Data.Points.Ascii/Parsing.cs b/src/Aardvark.Data.Points.Ascii/Parsing.cs index 8a05d6c..0e5a48d 100644 --- a/src/Aardvark.Data.Points.Ascii/Parsing.cs +++ b/src/Aardvark.Data.Points.Ascii/Parsing.cs @@ -152,7 +152,7 @@ CancellationToken ct var samples = optionalSamples; bounds.ExtendBy(new Box3d(samples.Positions)); Interlocked.Add(ref sampleCount, samples.Count); - var r = new Chunk(samples.Positions, samples.Colors, samples.Normals, samples.Intensities, samples.Classifications, samples.BoundingBox); + var r = new Chunk(samples.Positions, samples.Colors, samples.Normals, samples.Intensities, samples.Classifications, samples.PartIndices, samples.BoundingBox); Interlocked.Add(ref sampleCountYielded, r.Count); Interlocked.Add(ref totalBytesRead, buffer.Count); diff --git a/src/Aardvark.Data.Points.Base/Chunk.cs b/src/Aardvark.Data.Points.Base/Chunk.cs index dc7a88f..5c07419 100644 --- a/src/Aardvark.Data.Points.Base/Chunk.cs +++ b/src/Aardvark.Data.Points.Base/Chunk.cs @@ -256,6 +256,17 @@ public Chunk( ); } + #region part indices + + switch (partIndices) + { + case null: break; + case uint: break; + case IList: break; + case IList: break; + case IList: break; + default: throw new Exception($"Unexpected part indices type {partIndices.GetType().FullName}. Error fc9d196d-508e-4977-8f04-2167c71e38b0."); + } IList? qs1b = null; if (partIndices is IList _qs1b && _qs1b.Count != positions.Count) { @@ -287,6 +298,8 @@ public Chunk( ); } + #endregion + if (countMismatch) { var minCount = positions.Count; @@ -358,6 +371,7 @@ public Chunk Union(Chunk other) Append(Normals, other.Normals), Append(Intensities, other.Intensities), Append(Classifications, other.Classifications), + partIndices: PartIndexUtils.Union(PartIndices, other.PartIndices), Box.Union(BoundingBox, other.BoundingBox) ); } diff --git a/src/Aardvark.Data.Points.Base/PartIndexUtils.cs b/src/Aardvark.Data.Points.Base/PartIndexUtils.cs new file mode 100644 index 0000000..7ffb655 --- /dev/null +++ b/src/Aardvark.Data.Points.Base/PartIndexUtils.cs @@ -0,0 +1,74 @@ +/* + Aardvark Platform + Copyright (C) 2006-2023 Aardvark Platform Team + https://aardvark.graphics + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +using Aardvark.Base; +using System; +using System.Collections.Generic; +using System.Linq; + +#pragma warning disable CS1591 + +namespace Aardvark.Data.Points; + +/// +/// Utils for part indices. +/// +public static class PartIndexUtils +{ + /// + /// Returns union of two part indices (uint, IList of [byte|short|int]). + /// + public static object? Union(object? first, object? second) + { + checked + { + return (first, second) switch + { + (null, null) => null, + (object x, null) => x, + (null, object 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} and {second.GetType().FullName}. " + + $"Error 2f0672f5-8c6b-400b-8172-e83a30d70c28" + ) + }; + } + } + +} \ No newline at end of file diff --git a/src/Aardvark.Data.Points.Base/Storage.cs b/src/Aardvark.Data.Points.Base/Storage.cs index 56f0ae0..899780d 100644 --- a/src/Aardvark.Data.Points.Base/Storage.cs +++ b/src/Aardvark.Data.Points.Base/Storage.cs @@ -26,6 +26,21 @@ namespace Aardvark.Data.Points /// public class Storage : IDisposable { + private static readonly HashSet s_storages = new(); + + /// + /// No storage. All functions are NOP. + /// + public static readonly Storage None = new( + add: (_, _, _) => { }, + get: _ => null, + getSlice: (_, _, _) => null, + remove: _ => { }, + dispose: () => { }, + flush: () => { }, + cache: null + ); + private bool m_isDisposed = false; /// add(key, value, create) @@ -138,7 +153,5 @@ private static void Unregister(Storage storage) //} } } - - private static readonly HashSet s_storages = new(); } } \ No newline at end of file diff --git a/src/Aardvark.Data.Points.Ply/Aardvark.Data.Points.Ply.csproj b/src/Aardvark.Data.Points.Ply/Aardvark.Data.Points.Ply.csproj index e1c4aa1..47bdf8e 100644 --- a/src/Aardvark.Data.Points.Ply/Aardvark.Data.Points.Ply.csproj +++ b/src/Aardvark.Data.Points.Ply/Aardvark.Data.Points.Ply.csproj @@ -2,7 +2,7 @@ netstandard2.0 - latest + 10 enable true ..\.. diff --git a/src/Aardvark.Data.Points.Ply/PlyImport.cs b/src/Aardvark.Data.Points.Ply/PlyImport.cs index 7740f78..9e8ce64 100644 --- a/src/Aardvark.Data.Points.Ply/PlyImport.cs +++ b/src/Aardvark.Data.Points.Ply/PlyImport.cs @@ -59,7 +59,9 @@ public static IEnumerable Chunks(string filename, ParseConfig config) /// /// Parses PLY (.ply) file. /// +#pragma warning disable IDE0060 // Remove unused parameter public static IEnumerable Chunks(this Stream stream, long streamLengthInBytes, ParseConfig config) +#pragma warning restore IDE0060 // Remove unused parameter => PlyParser.Parse(stream, config.MaxChunkPointCount, config.Verbose ? (s => Report.Line(s)) : null).Chunks(); /// diff --git a/src/Aardvark.Geometry.PointSet/Import/ImportChunks.cs b/src/Aardvark.Geometry.PointSet/Import/ImportChunks.cs index e747408..df6a016 100644 --- a/src/Aardvark.Geometry.PointSet/Import/ImportChunks.cs +++ b/src/Aardvark.Geometry.PointSet/Import/ImportChunks.cs @@ -171,12 +171,12 @@ Chunk map(Chunk x, CancellationToken ct) // create LOD data if (config.Verbose) Report.BeginTimed("generate lod"); final = final.GenerateLod(config.WithRandomKey().WithProgressCallback(x => config.ProgressCallback(0.66 + x * 0.34))); - if (config.Storage?.GetPointCloudNode(final.Root.Value.Id) == null) throw new InvalidOperationException("Invariant 4d633e55-bf84-45d7-b9c3-c534a799242e."); + if (final.Root.Value != null && final.Root.Value.Id != Guid.Empty && config.Storage?.GetPointCloudNode(final.Root.Value.Id) == null) throw new InvalidOperationException("Invariant 4d633e55-bf84-45d7-b9c3-c534a799242e."); if (config.Verbose) Report.End(); // create final point set with specified key (or random key when no key is specified) var key = config.Key ?? Guid.NewGuid().ToString(); - final = new PointSet(config.Storage ?? throw new Exception($"No storage specified. Error 5b4ebfec-d418-4ddc-9c2f-646d270cf78c."), key, final.Root.Value.Id, config.OctreeSplitLimit); + final = new PointSet(config.Storage ?? throw new Exception($"No storage specified. Error 5b4ebfec-d418-4ddc-9c2f-646d270cf78c."), key, final.Root?.Value?.Id ?? Guid.Empty, config.OctreeSplitLimit); config.Storage.Add(key, final); return final; diff --git a/src/Aardvark.Geometry.PointSet/Import/MapReduce.cs b/src/Aardvark.Geometry.PointSet/Import/MapReduce.cs index 016bc54..38e59c2 100644 --- a/src/Aardvark.Geometry.PointSet/Import/MapReduce.cs +++ b/src/Aardvark.Geometry.PointSet/Import/MapReduce.cs @@ -88,6 +88,9 @@ public static PointSet MapReduce(this IEnumerable chunks, ImportConfig co var parts = new HashSet(pointsets); var final = pointsets.MapReduceParallel((first, second, ct2) => { + if (first .Root.Value == null) throw new Exception($"Expected first octree not to be null. Error c1ac4063-efaa-4c92-879c-7925509adee4."); + if (second.Root.Value == null) throw new Exception($"Expected second octree not to be null. Error 65289b58-2c36-46dd-91a6-81b42554d625."); + lock (parts) { if (!parts.Remove(first)) throw new InvalidOperationException("map reduce error"); @@ -123,6 +126,8 @@ public static PointSet MapReduce(this IEnumerable chunks, ImportConfig co Interlocked.Increment(ref doneCount); } + if (merged.Root.Value == null) throw new Exception($"Expected merged octree not to be null. Error b49cf692-2c1f-4067-b4cc-2960783fe141."); + //Console.WriteLine($"[MERGE CALLBACK][{id}] {(first.PointCount + second.PointCount) / (double)totalPointsToMerge,7:N3}"); lock (parts) diff --git a/src/Aardvark.Geometry.PointSet/Octrees/Delete.cs b/src/Aardvark.Geometry.PointSet/Octrees/Delete.cs index 115b494..651155a 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/Delete.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/Delete.cs @@ -29,28 +29,30 @@ namespace Aardvark.Geometry.Points public static class DeleteExtensions { /// - /// Returns new pointset with all points deleted which are inside. + /// Returns new pointset without points specified as inside. + /// Returns null, if no points are left. /// - public static PointSet? Delete(this PointSet? node, + public static PointSet? Delete(this PointSet? pointSet, Func isNodeFullyInside, Func isNodeFullyOutside, Func isPositionInside, Storage storage, CancellationToken ct ) { - if (node == null) return null; + if (pointSet == null) return null; - var root = Delete(node.Root.Value, isNodeFullyInside, isNodeFullyOutside, isPositionInside, storage, ct, node.SplitLimit); + var root = Delete(pointSet.Root.Value, isNodeFullyInside, isNodeFullyOutside, isPositionInside, storage, ct, pointSet.SplitLimit); if (root == null) return null; var newId = Guid.NewGuid().ToString(); - var result = new PointSet(node.Storage, newId, root.Id, node.SplitLimit); - node.Storage.Add(newId, result); + var result = new PointSet(pointSet.Storage, newId, root.Id, pointSet.SplitLimit); + pointSet.Storage.Add(newId, result); return result; } /// - /// Returns new tree with all points deleted which are inside. + /// Returns new octree with all points deleted which are inside. + /// Returns null, if no points are left. /// public static IPointCloudNode? Delete(this IPointCloudNode? root, Func isNodeFullyInside, @@ -97,7 +99,7 @@ int splitLimit var ns = root.HasNormals ? new List() : null; var js = root.HasIntensities ? new List() : null; var ks = root.HasClassifications ? new List() : null; - var oldPs = root.Positions.Value; + var oldPs = root.Positions.Value!; var oldCs = root.Colors?.Value; var oldNs = root.Normals?.Value; var oldIs = root.Intensities?.Value; @@ -343,8 +345,11 @@ int splitLimit } } } + /// - /// Returns new tree with all points deleted which are inside. Additionally tests visited points using classifications. + /// Returns new octree with all points deleted which are inside. + /// Additionally tests visited points using classifications. + /// Returns null, if no points are left. /// public static IPointCloudNode? DeleteWithClassifications(this IPointCloudNode? root, Func isNodeFullyInside, @@ -391,7 +396,7 @@ int splitLimit var ns = root.HasNormals ? new List() : null; var js = root.HasIntensities ? new List() : null; var ks = root.HasClassifications ? new List() : null; - var oldPs = root.Positions.Value; + var oldPs = root.Positions.Value!; var oldCs = root.Colors?.Value; var oldNs = root.Normals?.Value; var oldIs = root.Intensities?.Value; diff --git a/src/Aardvark.Geometry.PointSet/Octrees/IPointCloudNode.cs b/src/Aardvark.Geometry.PointSet/Octrees/IPointCloudNode.cs index 9b6ddaf..30e254e 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/IPointCloudNode.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/IPointCloudNode.cs @@ -94,6 +94,7 @@ public interface IPointCloudNode /// /// Node has no subnodes. /// + [MemberNotNullWhen(false, nameof(Subnodes))] bool IsLeaf { get; } /// diff --git a/src/Aardvark.Geometry.PointSet/Octrees/InMemoryPointSet.cs b/src/Aardvark.Geometry.PointSet/Octrees/InMemoryPointSet.cs index 6717d49..f301f45 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/InMemoryPointSet.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/InMemoryPointSet.cs @@ -64,8 +64,21 @@ public static InMemoryPointSet Build(IList ps, IList? cs, IList? switch (partIndices) { - case null: break; - case uint perCellPartIndex: data = data.Add(Durable.Octree.PerCellPartIndex1ui, perCellPartIndex); break; + case null : break; + case uint xs: data = data.Add(Durable.Octree.PerCellPartIndex1ui, xs ); break; + case byte[] xs: data = data.Add(Durable.Octree.PerPointPartIndex1b, xs); break; + case IList xs: data = data.Add(Durable.Octree.PerPointPartIndex1b, xs.ToArray()); break; + case IEnumerable xs: data = data.Add(Durable.Octree.PerPointPartIndex1b, xs.ToArray()); break; + case short[] xs: data = data.Add(Durable.Octree.PerPointPartIndex1s, xs); break; + case IList xs: data = data.Add(Durable.Octree.PerPointPartIndex1s, xs.ToArray()); break; + case IEnumerable xs: data = data.Add(Durable.Octree.PerPointPartIndex1s, xs.ToArray()); break; + case int[] xs: data = data.Add(Durable.Octree.PerPointPartIndex1i, xs); break; + case IList xs: data = data.Add(Durable.Octree.PerPointPartIndex1i, xs.ToArray()); break; + case IEnumerable xs: data = data.Add(Durable.Octree.PerPointPartIndex1i, xs.ToArray()); break; + + default: throw new Exception( + $"Unknown part indices type {partIndices.GetType().FullName}. Error 9869c0df-de22-4385-b39f-2b55a8c64f13." + ); } return new InMemoryPointSet(data, rootBounds, octreeSplitLimit); diff --git a/src/Aardvark.Geometry.PointSet/Octrees/Inline.cs b/src/Aardvark.Geometry.PointSet/Octrees/Inline.cs index 67e0aac..f63aefc 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/Inline.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/Inline.cs @@ -263,7 +263,7 @@ public static InlinedNodes EnumerateOctreeInlined( public static InlinedNodes EnumerateOctreeInlined( this PointSet pointset, InlineConfig config ) - => pointset.Root.Value.EnumerateOctreeInlined(config); + => pointset.Root.Value!.EnumerateOctreeInlined(config); /// /// Enumerate inlined (self-contained, no external data is referenced) octree nodes. @@ -482,10 +482,10 @@ static byte[] rescaleIntensities(int[] js32) subnodeGuids = subnodes.Map(x => x.HasValue && x.Value.hasValue ? x.Value.value!.Id : Guid.Empty); } - ps = node.Positions.Value; + ps = node.Positions.Value!; if (hasColors) cs = node.Colors!.Value; if (hasClassifications) ks = node.Classifications!.Value; - if (hasIntensities) js = rescaleIntensities(node.Intensities!.Value); + if (hasIntensities) js = rescaleIntensities(node.Intensities!.Value!); if (isNotLeaf) subnodeGuids = subnodes.Map(x => x.HasValue && x.Value.hasValue ? x.Value.value!.Id : Guid.Empty); } diff --git a/src/Aardvark.Geometry.PointSet/Octrees/Lod.cs b/src/Aardvark.Geometry.PointSet/Octrees/Lod.cs index 9f56cea..fd67de0 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/Lod.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/Lod.cs @@ -250,21 +250,21 @@ internal static async Task GenerateLod(this PointSetNode self, { kd = await self.Positions.Value.BuildKdTreeAsync(); var kdKey = Guid.NewGuid(); - store.Add(kdKey, kd.Data); + store?.Add(kdKey, kd.Data); upsertData = upsertData.Add(Durable.Octree.PointRkdTreeFDataReference, kdKey); } if (!self.HasNormals) { var ns = await self.Positions.Value.EstimateNormalsAsync(16, kd); - if (ns.Length != self.Positions.Value.Length) throw new Exception(); + if (ns.Length != self.Positions.Value!.Length) throw new Exception(); var nsId = Guid.NewGuid(); - store.Add(nsId, ns); + store?.Add(nsId, ns); upsertData = upsertData.Add(Durable.Octree.Normals3fReference, nsId); } else { - if (self.Normals!.Value.Length != self.PointCountCell) throw new Exception(); + if (self.Normals!.Value!.Length != self.PointCountCell) throw new Exception(); } if (upsertData.Count > 0) self = self.With(upsertData); @@ -330,7 +330,7 @@ bool subnodesHaveAttribute(Func check, string kind) var lodPs = AggregateSubPositions(counts, aggregateCount, self.Center, subcenters, subcells.Map(x => x?.Positions?.Value)); if (lodPs.Any(p => p.IsNaN)) throw new Exception("One or more positions are NaN."); var lodPsKey = Guid.NewGuid(); - store.Add(lodPsKey, lodPs); + store?.Add(lodPsKey, lodPs); upsertData = upsertData .Add(Durable.Octree.PositionsLocal3fReference, lodPsKey) .Add(Durable.Octree.PointCountCell, lodPs.Length) @@ -339,7 +339,7 @@ bool subnodesHaveAttribute(Func check, string kind) // ... kd-tree ... var lodKd = await lodPs.BuildKdTreeAsync(); var lodKdKey = Guid.NewGuid(); - store.Add(lodKdKey, lodKd.Data); + store?.Add(lodKdKey, lodKd.Data); upsertData = upsertData.Add(Durable.Octree.PointRkdTreeFDataReference, lodKdKey); // .. normals ... @@ -348,7 +348,7 @@ bool subnodesHaveAttribute(Func check, string kind) $"Inconsistent lod-normals length {lodNs.Length}. Should be {lodPs.Length}. Error 806dfe30-4faa-46b1-a2a3-f50e336cbe67." ); var lodNsKey = Guid.NewGuid(); - store.Add(lodNsKey, lodNs); + store?.Add(lodNsKey, lodNs); upsertData = upsertData.Add(Durable.Octree.Normals3fReference, lodNsKey); void addAttributeByRef(string kind, Durable.Def def, Func has, Func getData) @@ -362,7 +362,7 @@ void addAttributeByRef(string kind, Durable.Def def, Func ps, List? cs, List? ns, List? js, List? ks) { if (self == null) return 0; - else if (self.IsLeaf) + + if (self.IsLeaf) { if (self.HasPositions && ps != null) { @@ -37,20 +38,20 @@ internal static int CollectEverything(IPointCloudNode self, List ps, List off + (V3d)p)); } - if (self.HasColors && cs != null) cs.AddRange(self.Colors!.Value); - if (self.HasNormals && ns != null) ns.AddRange(self.Normals.Value); - if (self.HasIntensities && js != null) js.AddRange(self.Intensities.Value); - if (self.HasClassifications && ks != null) ks.AddRange(self.Classifications!.Value); + if (self.HasColors && cs != null) cs.AddRange(self.Colors.Value); + if (self.HasNormals && ns != null) ns.AddRange(self.Normals.Value); + if (self.HasIntensities && js != null) js.AddRange(self.Intensities.Value); + if (self.HasClassifications && ks != null) ks.AddRange(self.Classifications.Value); return 1; } else { var leafs = 0; - foreach (var x in self.Subnodes!) + foreach (var x in self.Subnodes) { if (x != null) { - leafs += CollectEverything(x.Value, ps, cs, ns, js, ks); + leafs += CollectEverything(x!.Value!, ps, cs, ns, js, ks); } } return leafs; @@ -342,9 +343,9 @@ public static (IPointCloudNode, bool) Merge(this IPointCloudNode a, IPointCloudN ; storage.Add(psId, ps ); data = data.Add(Durable.Octree.PositionsLocal3fReference, psId); - if (nsId.HasValue) { storage.Add(nsId.Value, ns!); data = data.Add(Durable.Octree.Normals3fReference, nsId.Value); } - if (csId.HasValue) { storage.Add(csId.Value, cs!); data = data.Add(Durable.Octree.Colors4bReference, csId.Value); } - if (jsId.HasValue) { storage.Add(jsId.Value, js!); data = data.Add(Durable.Octree.Intensities1iReference, jsId.Value); } + if (nsId.HasValue) { storage.Add(nsId.Value, ns!); data = data.Add(Durable.Octree.Normals3fReference , nsId.Value); } + if (csId.HasValue) { storage.Add(csId.Value, cs!); data = data.Add(Durable.Octree.Colors4bReference , csId.Value); } + if (jsId.HasValue) { storage.Add(jsId.Value, js!); data = data.Add(Durable.Octree.Intensities1iReference , jsId.Value); } if (ksId.HasValue) { storage.Add(ksId.Value, ks!); data = data.Add(Durable.Octree.Classifications1bReference, ksId.Value); } //if (kdId.HasValue) { storage.Add(kdId.Value, kd.Data); data = data.Add(Durable.Octree.PointRkdTreeFDataReference, kdId.Value); } diff --git a/src/Aardvark.Geometry.PointSet/Octrees/PointSet.cs b/src/Aardvark.Geometry.PointSet/Octrees/PointSet.cs index aa12a03..cb29f2c 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/PointSet.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/PointSet.cs @@ -15,6 +15,7 @@ You should have received a copy of the GNU Affero General Public License using Aardvark.Data.Points; using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; @@ -30,24 +31,25 @@ public class PointSet /// /// The empty pointset. /// - public static readonly PointSet Empty = new(null!, "PointSet.Empty"); + public static readonly PointSet Empty = new(Storage.None, "PointSet.Empty"); #region Construction /// /// Creates PointSet from given points and colors. /// - public static PointSet Create(Storage storage, string key, + public static PointSet Create(Storage storage, string pointSetId, IList positions, IList colors, IList normals, IList intensities, IList classifications, object? partIndices, int octreeSplitLimit, bool generateLod, bool isTemporaryImportNode, CancellationToken ct = default ) { - if (key == null) throw new ArgumentNullException(nameof(key)); + if (storage == null) throw new ArgumentNullException(nameof(storage)); + if (pointSetId == null) throw new ArgumentNullException(nameof(pointSetId)); var bounds = new Box3d(positions); var builder = InMemoryPointSet.Build(positions, colors, normals, intensities, classifications, partIndices, new Cell(bounds), octreeSplitLimit); var root = builder.ToPointSetNode(storage, isTemporaryImportNode); - var result = new PointSet(storage, key, root.Id, octreeSplitLimit); + var result = new PointSet(storage, pointSetId: pointSetId, rootCellId: root.Id, octreeSplitLimit); if (result.Root.Value == null) throw new InvalidOperationException("Invariant 5492d57b-add1-48bf-9721-5087c957d81e."); @@ -61,16 +63,36 @@ public static PointSet Create(Storage storage, string key, result = result.GenerateLod(config); } + checked + { + switch (partIndices) + { + case null : break; + case byte x : result = result.WithPartIndexRange(new(x, x)); break; + case short x : result = result.WithPartIndexRange(new(x, x)); break; + case int x : result = result.WithPartIndexRange(new(x, x)); break; + case uint x : result = result.WithPartIndexRange(new((int)x, (int)x)); break; + case IList xs: result = result.WithPartIndexRange(new(xs.Select(x => (int)x))); break; + case IList xs: result = result.WithPartIndexRange(new(xs.Select(x => (int)x))); break; + case IList xs: result = result.WithPartIndexRange(new(xs.Select(x => x))); break; + case IList xs: result = result.WithPartIndexRange(new(xs.Select(x => (int)x))); break; + + default: throw new Exception( + $"Unknown part indices type {partIndices.GetType().FullName}. " + + $"Error 8b6b8202-dc6c-4c2c-89a3-40f24c7eee5c." + ); + } + } return result; } /// /// Creates pointset from given root cell. /// - public PointSet(Storage storage, string key, Guid rootCellId, int splitLimit) + public PointSet(Storage storage, string pointSetId, Guid rootCellId, int splitLimit) { - Storage = storage; - Id = key ?? throw new ArgumentNullException(nameof(key)); + Storage = storage ?? throw new ArgumentNullException(nameof(storage)); + Id = pointSetId ?? throw new ArgumentNullException(nameof(pointSetId)); SplitLimit = splitLimit; Root = new PersistentRef(rootCellId.ToString(), storage.GetPointCloudNode!, @@ -85,7 +107,7 @@ public PointSet(Storage storage, string key, IPointCloudNode root, int splitLimi { if (root == null) throw new ArgumentNullException(nameof(root)); - Storage = storage; + Storage = storage ?? throw new ArgumentNullException(nameof(storage)); Id = key ?? throw new ArgumentNullException(nameof(key)); SplitLimit = splitLimit; @@ -97,11 +119,11 @@ public PointSet(Storage storage, string key, IPointCloudNode root, int splitLimi /// public PointSet(Storage storage, string key) { - Storage = storage; + Storage = storage ?? throw new ArgumentNullException(nameof(storage)); Id = key ?? throw new ArgumentNullException(nameof(key)); SplitLimit = 0; - Root = null!; + Root = new(id: "", _ => PointSetNode.Empty, _ => (true, PointSetNode.Empty)); } #endregion @@ -135,7 +157,7 @@ public JsonNode ToJson() => JsonSerializer.SerializeToNode(new { Id, RootCellId = Root.Id, - OctreeId = Root.Id, + OctreeId = Root.Id, // backwards compatibility SplitLimit, PartIndexRange })!; @@ -147,7 +169,7 @@ public static PointSet Parse(JsonNode json, Storage storage) var o = json.AsObject() ?? throw new Exception($"Expected JSON object, but found {json}."); var octreeId = (string?)o["OctreeId"] ?? (string?)o["RootCellId"]; - var octree = octreeId != null + var octreeRef = octreeId != null ? new PersistentRef(octreeId, storage.GetPointCloudNode!, storage.TryGetPointCloudNode) : null ; @@ -166,7 +188,8 @@ public static PointSet Parse(JsonNode json, Storage storage) ; // - return new PointSet(storage, id, octree?.Value ?? PointSetNode.Empty, splitLimit).WithPartIndexRange(partIndexRange); + var octree = octreeRef?.Value; + return new PointSet(storage, id, octree ?? PointSetNode.Empty, splitLimit).WithPartIndexRange(partIndexRange); } #endregion @@ -181,7 +204,7 @@ public static PointSet Parse(JsonNode json, Storage storage) /// /// Returns true if pointset is empty. /// - public bool IsEmpty => Root == null || Root.Id == Guid.Empty.ToString(); + public bool IsEmpty => Root.Id == "" || Root.Id == Guid.Empty.ToString(); /// /// Gets total number of points in dataset. @@ -191,7 +214,7 @@ public static PointSet Parse(JsonNode json, Storage storage) /// /// Gets bounds of dataset root cell. /// - public Box3d Bounds => Root?.Value?.Cell.BoundingBox ?? Box3d.Invalid; + public Box3d Bounds => Root.Value!.Cell.IsValid ? Root.Value.Cell.BoundingBox : Box3d.Invalid; /// /// Gets exact bounding box of all points in pointcloud. @@ -202,7 +225,7 @@ public Box3d BoundingBox { try { - return Root.Value.BoundingBoxExactGlobal; + return Root.Value!.BoundingBoxExactGlobal; } catch (NullReferenceException) { @@ -212,22 +235,22 @@ public Box3d BoundingBox } /// - public bool HasColors => Root != null && Root.Value.HasColors; + public bool HasColors => Root != null && Root.Value!.HasColors; /// - public bool HasIntensities => Root != null && Root.Value.HasIntensities; + public bool HasIntensities => Root != null && Root.Value!.HasIntensities; /// - public bool HasClassifications => Root != null && Root.Value.HasClassifications; + public bool HasClassifications => Root != null && Root.Value!.HasClassifications; /// - public bool HasKdTree => Root != null && Root.Value.HasKdTree; + public bool HasKdTree => Root != null && Root.Value!.HasKdTree; /// - public bool HasNormals => Root != null && Root.Value.HasNormals; + public bool HasNormals => Root != null && Root.Value!.HasNormals; /// - public bool HasPositions => Root != null && Root.Value.HasPositions; + public bool HasPositions => Root != null && Root.Value!.HasPositions; /// @@ -253,7 +276,7 @@ public PointSet Merge(PointSet other, Action pointsMergedCallback, ImportC } else { - throw new InvalidOperationException($"Cannot merge {Root.Value.GetType()} with {other.Root.Value.GetType()}."); + throw new InvalidOperationException($"Cannot merge {Root.Value!.GetType()} with {other.Root.Value!.GetType()}. Error cf3a17bf-c3c7-46de-9fb7-f08243992ff0."); } } diff --git a/src/Aardvark.Geometry.PointSet/Octrees/PointSetNode.cs b/src/Aardvark.Geometry.PointSet/Octrees/PointSetNode.cs index c0e9658..22d0e58 100644 --- a/src/Aardvark.Geometry.PointSet/Octrees/PointSetNode.cs +++ b/src/Aardvark.Geometry.PointSet/Octrees/PointSetNode.cs @@ -20,7 +20,6 @@ You should have received a copy of the GNU Affero General Public License using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.Json.Serialization; @@ -49,17 +48,17 @@ public class PointSetNode : IPointCloudNode public static readonly PointSetNode Empty = new( null!, writeToStore: false, (Durable.Octree.NodeId, Guid.Empty), - (Durable.Octree.Cell, Cell.Unit), - (Durable.Octree.BoundingBoxExactLocal, Box3f.Unit - new V3f(0.5f)), - (Durable.Octree.BoundingBoxExactGlobal, Box3d.Unit), + (Durable.Octree.Cell, Cell.Invalid), + (Durable.Octree.BoundingBoxExactLocal, Box3f.Invalid), + (Durable.Octree.BoundingBoxExactGlobal, Box3d.Invalid), (Durable.Octree.PointCountCell, 0), (Durable.Octree.PointCountTreeLeafs, 0L), - (Durable.Octree.PositionsLocal3fReference, Guid.Empty), - (Durable.Octree.PointRkdTreeFDataReference, Guid.Empty), - (Durable.Octree.PositionsLocal3fCentroid, V3f.Zero), - (Durable.Octree.PositionsLocal3fDistToCentroidStdDev, 0.0f), - (Durable.Octree.AveragePointDistance, 0.0f), - (Durable.Octree.AveragePointDistanceStdDev, 0.0f), + //(Durable.Octree.PositionsLocal3fReference, Guid.Empty), + //(Durable.Octree.PointRkdTreeFDataReference, Guid.Empty), + (Durable.Octree.PositionsLocal3fCentroid, V3f.NaN), + (Durable.Octree.PositionsLocal3fDistToCentroidStdDev, float.NaN), + (Durable.Octree.AveragePointDistance, float.NaN), + (Durable.Octree.AveragePointDistanceStdDev, float.NaN), (Durable.Octree.MinTreeDepth, 0), (Durable.Octree.MaxTreeDepth, 0) ); @@ -82,7 +81,7 @@ public PointSetNode(Storage storage, bool writeToStore, params (Durable.Def, obj /// public PointSetNode( ImmutableDictionary data, - Storage storage, bool writeToStore + Storage? storage, bool writeToStore ) { if (!data.ContainsKey(Durable.Octree.NodeId)) throw new ArgumentException( @@ -95,6 +94,8 @@ public PointSetNode( var isObsoleteFormat = data.ContainsKey(Durable.Octree.PointRkdTreeDDataReference); + storage ??= Storage.None; + Storage = storage; Data = data; @@ -109,7 +110,7 @@ public PointSetNode( Corners = bboxCell.ComputeCorners(); #if DEBUG - if (PositionsAbsolute != null) // if not PointSetNode.Empty + if (PositionsAbsolute?.Length > 0) // if not PointSetNode.Empty { // Invariant: bounding box of global positions MUST BE CONTAINED within bounding box of node cell var bb = new Box3d(PositionsAbsolute); @@ -136,7 +137,7 @@ public PointSetNode( for (var i = 0; i < 8; i++) { if (subNodeIds[i] == Guid.Empty) continue; - var pRef = new PersistentRef(subNodeIds[i].ToString(), storage.GetPointCloudNode!, storage.TryGetPointCloudNode); + var pRef = new PersistentRef(subNodeIds[i].ToString(), storage.GetPointCloudNode, storage.TryGetPointCloudNode); Subnodes[i] = pRef; #if DEBUG @@ -190,7 +191,7 @@ public PointSetNode( { if (!HasBoundingBoxExactLocal) // why only for new format? (!isObsoleteFormat && !HasBoundingBoxExactLocal) { - var bboxExactLocal = Positions.Value.Length > 0 ? new Box3f(Positions.Value) : Box3f.Invalid; + var bboxExactLocal = Positions.Value!.Length > 0 ? new Box3f(Positions.Value) : Box3f.Invalid; Data = Data.Add(Durable.Octree.BoundingBoxExactLocal, bboxExactLocal); } @@ -208,7 +209,7 @@ public PointSetNode( } else { - var bboxExactGlobal = new Box3d(Subnodes.Where(x => x != null).Select(x => x.Value.BoundingBoxExactGlobal)); + var bboxExactGlobal = new Box3d(Subnodes.Where(x => x != null).Select(x => x!.Value!.BoundingBoxExactGlobal)); Data = Data.Add(Durable.Octree.BoundingBoxExactGlobal, bboxExactGlobal); } } @@ -220,7 +221,7 @@ public PointSetNode( if (!Has(Durable.Octree.PointCountCell)) { - Data = Data.Add(Durable.Octree.PointCountCell, HasPositions ? Positions.Value.Length : 0); + Data = Data.Add(Durable.Octree.PointCountCell, HasPositions ? Positions.Value!.Length : 0); } #endregion @@ -235,7 +236,7 @@ public PointSetNode( } else { - Data = Data.Add(Durable.Octree.PointCountTreeLeafs, Subnodes.Where(x => x != null).Sum(x => x.Value.PointCountTree)); + Data = Data.Add(Durable.Octree.PointCountTreeLeafs, Subnodes.Where(x => x != null).Sum(x => x.Value!.PointCountTree)); } } @@ -296,7 +297,7 @@ public PointSetNode( { var r = Subnodes![i]; if (r == null) continue; - var n = r.Value; + var n = r.Value!; min = Math.Min(min, 1 + n.MinTreeDepth); max = Math.Max(max, 1 + n.MaxTreeDepth); } @@ -331,11 +332,11 @@ public PointSetNode( #endregion - if ((HasPositions && Positions.Value.Length != PointCountCell) || - (HasColors && Colors!.Value.Length != PointCountCell) || - (HasNormals && Normals!.Value.Length != PointCountCell) || - (HasIntensities && Intensities!.Value.Length != PointCountCell) || - (HasClassifications && Classifications!.Value.Length != PointCountCell) + if ((HasPositions && Positions.Value!.Length != PointCountCell) || + (HasColors && Colors.Value!.Length != PointCountCell) || + (HasNormals && Normals.Value!.Length != PointCountCell) || + (HasIntensities && Intensities.Value!.Length != PointCountCell) || + (HasClassifications && Classifications.Value!.Length != PointCountCell) ) { #if DEBUG @@ -366,30 +367,33 @@ public PointSetNode( if (PointCountCell != PointCountTree) throw new InvalidOperationException("Invariant 9464f38c-dc98-4d68-a8ac-0baed9f182b4."); - if (!( - Has(Durable.Octree.PositionsLocal3fReference) || - Has(Durable.Octree.PositionsLocal3f) || - Has(Durable.Octree.PositionsLocal3b) || - Has(Durable.Octree.PositionsLocal3us) || - Has(Durable.Octree.PositionsLocal3ui) || - Has(Durable.Octree.PositionsLocal3ul) - )) - throw new ArgumentException("Invariant 663c45a4-1286-45ba-870c-fb4ceebdf318."); - - if (Has(Durable.Octree.PositionsLocal3fReference) && PositionsId == null) - throw new InvalidOperationException("Invariant ba64ffe9-ada4-4fff-a4e9-0916c1cc9992."); - - #if !READONLY - if (KdTreeId == null && !Has(TemporaryImportNode)) - throw new InvalidOperationException("Invariant 606e8a7b-6e75-496a-bc2a-dfbe6e2c9b10."); - #endif + if (Id != Guid.Empty) + { + if (!( + Has(Durable.Octree.PositionsLocal3fReference) || + Has(Durable.Octree.PositionsLocal3f) || + Has(Durable.Octree.PositionsLocal3b) || + Has(Durable.Octree.PositionsLocal3us) || + Has(Durable.Octree.PositionsLocal3ui) || + Has(Durable.Octree.PositionsLocal3ul) + )) + throw new ArgumentException("Invariant 663c45a4-1286-45ba-870c-fb4ceebdf318."); + + if (Has(Durable.Octree.PositionsLocal3fReference) && PositionsId == null) + throw new InvalidOperationException("Invariant ba64ffe9-ada4-4fff-a4e9-0916c1cc9992."); + + #if !READONLY + if (KdTreeId == null && !Has(TemporaryImportNode)) + throw new InvalidOperationException("Invariant 606e8a7b-6e75-496a-bc2a-dfbe6e2c9b10."); + #endif + } } #endif PointRkdTreeF LoadKdTree(string key) { var value = Storage.GetPointRkdTreeFData(key); - var ps = Positions.Value; + var ps = Positions.Value!; return new PointRkdTreeF( 3, ps.Length, ps, (xs, i) => xs[(int)i], (v, i) => (float)v[i], @@ -403,7 +407,7 @@ PointRkdTreeF LoadKdTree(string key) { var (ok, value) = Storage.TryGetPointRkdTreeFData(key); if (ok == false) return (false, default); - var ps = Positions.Value; + var ps = Positions.Value!; return (true, new PointRkdTreeF( 3, ps.Length, ps, (xs, i) => xs[(int)i], (v, i) => (float)v[i], @@ -416,7 +420,7 @@ PointRkdTreeF LoadKdTree(string key) PointRkdTreeF LoadKdTreeObsolete(string key) { var value = Storage.GetPointRkdTreeFDataFromD(key); - var ps = Positions.Value; + var ps = Positions.Value!; return new PointRkdTreeF( 3, ps.Length, ps, (xs, i) => xs[(int)i], (v, i) => (float)v[i], @@ -430,7 +434,7 @@ PointRkdTreeF LoadKdTreeObsolete(string key) { var (ok, value) = Storage.TryGetPointRkdTreeFDataFromD(key); if (ok == false) return (false, default); - var ps = Positions.Value; + var ps = Positions.Value!; return (true, new PointRkdTreeF( 3, ps.Length, ps, (xs, i) => xs[(int)i], (v, i) => (float)v[i], @@ -447,7 +451,7 @@ PointRkdTreeF LoadKdTreeObsolete(string key) public PointSetNode WriteToStore() { this.CheckDerivedAttributes(); - Storage.Add(Id.ToString(), this); + Storage?.Add(Id.ToString(), this); return this; } @@ -620,7 +624,8 @@ public PersistentRef Positions return new PersistentRef(Guid.Empty, _ => ps, _ => (true, ps)); } else - throw new Exception("Node has no positions. Error cef41b10-945b-475c-add6-df93b2a24945."); + return null!; + //return new(id: "", _ => Array.Empty(), _ => (true, Array.Empty())); } } @@ -628,7 +633,7 @@ public PersistentRef Positions /// Point positions (absolute), or null if no positions. /// [JsonIgnore] - public V3d[] PositionsAbsolute => Positions.Value.Map(p => new V3d(Center.X + p.X, Center.Y + p.Y, Center.Z + p.Z)); + public V3d[] PositionsAbsolute => Positions?.Value.Map(p => new V3d(Center.X + p.X, Center.Y + p.Y, Center.Z + p.Z))!; #endregion @@ -1244,7 +1249,7 @@ public Box3d BoundingBoxApproximate if(HasPositions) { - var ps = Positions.Value; + var ps = Positions.Value!; if (ps.Length > 0) return (Box3d)(new Box3f(ps)) + Center; } diff --git a/src/Aardvark.Geometry.PointSet/Utils/PersistentRef.cs b/src/Aardvark.Geometry.PointSet/Utils/PersistentRef.cs index 06ce14f..4858b42 100644 --- a/src/Aardvark.Geometry.PointSet/Utils/PersistentRef.cs +++ b/src/Aardvark.Geometry.PointSet/Utils/PersistentRef.cs @@ -14,7 +14,6 @@ You should have received a copy of the GNU Affero General Public License using System; using System.Diagnostics.CodeAnalysis; -using System.Threading; namespace Aardvark.Geometry.Points { @@ -24,7 +23,7 @@ namespace Aardvark.Geometry.Points public class PersistentRef where T : notnull { - private readonly Func f_get; + private readonly Func f_get; private readonly Func f_tryGet; /// @@ -36,34 +35,13 @@ public PersistentRef(Guid id, Func get, Func tryG /// /// - public PersistentRef(string id, Func get, Func tryGet) + public PersistentRef(string id, Func get, Func tryGet) { Id = id; //?? throw new ArgumentNullException(nameof(id)); f_get = get ?? throw new ArgumentNullException(nameof(get)); f_tryGet = tryGet ?? throw new ArgumentNullException(nameof(tryGet)); } - ///// - ///// - //public PersistentRef Cast() where A : notnull - //{ - // var get = f_get; - // var tryGet = f_tryGet; - // return new PersistentRef( - // Id, - // (s => (A)(object)get(s)), - // (s => { var (w, t) = tryGet(s); return w ? (w, (A)(object)t) : (false, default(A)); }) - // ); - - //} - - ///// - ///// - //public static PersistentRef FromValue(T value) - //{ - // return new PersistentRef(null, s => value, s => (true, value)); - //} - /// /// public string Id { get; } @@ -85,13 +63,6 @@ public bool TryGetValue([NotNullWhen(true)]out T? value) /// /// - public T Value - { - get - { - var result = f_get(Id); - return result == null ? throw new InvalidOperationException("Invariant e73282a1-45c0-4cb3-bccf-e6d416163abc.") : result; - } - } + public T? Value => f_get(Id); } } diff --git a/src/Aardvark.Geometry.PointSet/Utils/StorageExtensions.cs b/src/Aardvark.Geometry.PointSet/Utils/StorageExtensions.cs index 0e40a87..b69c430 100644 --- a/src/Aardvark.Geometry.PointSet/Utils/StorageExtensions.cs +++ b/src/Aardvark.Geometry.PointSet/Utils/StorageExtensions.cs @@ -561,7 +561,8 @@ public static void Add(this Storage storage, string key, PointRkdTreeFData data) /// public static PointRkdTreeFData? GetPointRkdTreeFData(this Storage storage, string key) { - if (storage.HasCache && storage.Cache.TryGetValue(key, out object o)) return (PointRkdTreeFData)o; + if (storage == null) return null; + if (storage.HasCache == true && storage.Cache.TryGetValue(key, out object o)) return (PointRkdTreeFData)o; var buffer = storage.f_get(key); if (buffer == null) return null; @@ -709,7 +710,7 @@ public static void Add(this Storage storage, string key, IPointCloudNode data) /// public static IPointCloudNode? GetPointCloudNode(this Storage storage, string key) { - //if (key == null) return null; + if (key == null) return null; if (storage.HasCache && storage.Cache.TryGetValue(key, out object o)) { @@ -838,7 +839,7 @@ public static bool TryGetOctree(this Storage storage, string key, [NotNullWhen(t { var ps = storage.GetPointSet(key); if (ps == null) { root = null; return false; } - root = ps.Root.Value; + root = ps.Root.Value!; return true; } catch diff --git a/src/Ply.Net/Ply.Net.csproj b/src/Ply.Net/Ply.Net.csproj index f408ec1..d66aaaf 100644 --- a/src/Ply.Net/Ply.Net.csproj +++ b/src/Ply.Net/Ply.Net.csproj @@ -2,7 +2,7 @@ netstandard2.0 - latest + 10 enable True ..\..