/// <summary> /// Points within given distance of a line segment (at most 1000). /// </summary> public static IEnumerable <PointsNearObject <Line3d> > QueryPointsNearLineSegment( this PointSet self, Line3d lineSegment, double maxDistanceToRay ) => QueryPointsNearLineSegment(self.Root.Value, lineSegment, maxDistanceToRay);
/// <summary> /// Count points approximately NOT within maxDistance of given plane. /// Result is always equal or greater than exact number. /// Faster than CountPointsNotNearPlane. /// </summary> public static long CountPointsApproximatelyNotNearPlane( this PointSet self, Plane3d plane, double maxDistance, int minCellExponent = int.MinValue ) => CountPointsApproximatelyNotNearPlane(self.Root.Value, plane, maxDistance, minCellExponent);
/// <summary> /// All points inside axis-aligned box (including boundary). /// </summary> public static IEnumerable <Chunk> QueryPointsInsideBox( this PointSet self, Box3d query, int minCellExponent = int.MinValue ) => QueryPointsInsideBox(self.Root.Value, query, minCellExponent);
/// <summary> /// Counts points outside axis-aligned box. /// </summary> public static long CountPointsOutsideBox( this PointSet self, Box3d query, int minCellExponent = int.MinValue ) => CountPointsOutsideBox(self.Root.Value, query, minCellExponent);
/// <summary> /// Enumerates (chunked) all points in pointset. /// </summary> public static IEnumerable <Chunk> QueryAllPoints(this PointSet self) => QueryAllPoints(self.Root.Value);
/// <summary> /// Points within given distance of a point. /// </summary> public static PointsNearObject <V3d> QueryPointsNearPoint( this PointSet self, V3d query, double maxDistanceToPoint, int maxCount ) => QueryPointsNearPoint(self.Root.Value, query, maxDistanceToPoint, maxCount);
/// <summary> /// Count points NOT within maxDistance of given polygon. /// </summary> public static long CountPointsNotNearPolygon( this PointSet self, Polygon3d polygon, double maxDistance, int minCellExponent = int.MinValue ) => CountPointsNotNearPolygon(self.Root.Value, polygon, maxDistance, minCellExponent);
/// <summary> /// Counts points outside convex hull. /// </summary> internal static long CountPointsOutsideConvexHull( this PointSet self, Hull3d query, int minCellExponent = int.MinValue ) => CountPointsOutsideConvexHull(self.Root.Value, query, minCellExponent);
/// <summary> /// Finds deepest octree level which still contains less than given number of points within given bounds. /// </summary> public static int GetMaxOctreeLevelWithLessThanGivenPointCount( this PointSet self, long maxPointCount, Box3d bounds ) => GetMaxOctreeLevelWithLessThanGivenPointCount(self.Root.Value, maxPointCount, bounds);
/// <summary> /// Maps a sequence of point chunks to point sets, which are then reduced to one single point set. /// </summary> public static PointSet MapReduce(this IEnumerable <Chunk> chunks, ImportConfig config) { //var foo = chunks.ToArray(); var totalChunkCount = 0; var totalPointCountInChunks = 0L; Action <double> progress = x => config.ProgressCallback(x * 0.5); #region MAP: create one PointSet for each chunk var pointsets = chunks .MapParallel((chunk, ct2) => { Interlocked.Add(ref totalPointCountInChunks, chunk.Count); progress(Math.Sqrt(1.0 - 1.0 / Interlocked.Increment(ref totalChunkCount))); var builder = InMemoryPointSet.Build(chunk, config.OctreeSplitLimit); var root = builder.ToPointSetNode(config.Storage, isTemporaryImportNode: true); var id = $"Aardvark.Geometry.PointSet.{Guid.NewGuid()}.json"; var pointSet = new PointSet(config.Storage, id, root.Id, config.OctreeSplitLimit); return(pointSet); }, config.MaxDegreeOfParallelism, null, config.CancellationToken ) .ToList() ; ; if (config.Verbose) { Console.WriteLine($"[MapReduce] pointsets : {pointsets.Count}"); Console.WriteLine($"[MapReduce] totalPointCountInChunks: {totalPointCountInChunks}"); } #endregion #region REDUCE: pairwise octree merge until a single (final) octree remains progress = x => config.ProgressCallback(0.5 + x * 0.5); var i = 0; var fractionalProgress = new Dictionary <int, double>(); var totalPointsToMerge = pointsets.Sum(x => x.PointCount); if (config.Verbose) { Console.WriteLine($"[MapReduce] totalPointsToMerge: {totalPointsToMerge}"); } var totalPointSetsCount = pointsets.Count; if (totalPointSetsCount == 0) { var empty = new PointSet(config.Storage, config.Key ?? Guid.NewGuid().ToString()); config.Storage.Add(config.Key, empty); return(empty); } var doneCount = 0; var parts = new HashSet <PointSet>(pointsets); var final = pointsets.MapReduceParallel((first, second, ct2) => { lock (parts) { if (!parts.Remove(first)) { throw new InvalidOperationException("map reduce error"); } if (!parts.Remove(second)) { throw new InvalidOperationException("map reduce error"); } } var id = Interlocked.Increment(ref i); var firstPlusSecondPointCount = first.PointCount + second.PointCount; var lastN = 0L; var merged = first.Merge(second, n => { //Console.WriteLine($"[MERGE CALLBACK][{id}] {n:N0}"); if (n > lastN) { lastN = n; var p = 0.0; lock (fractionalProgress) { fractionalProgress[id] = n / (double)firstPlusSecondPointCount; p = 1.0 / (totalPointSetsCount - (doneCount + fractionalProgress.Values.Sum())); } progress(p); } }, config.WithCancellationToken(ct2) ); lock (fractionalProgress) { fractionalProgress.Remove(id); Interlocked.Increment(ref doneCount); } //Console.WriteLine($"[MERGE CALLBACK][{id}] {(first.PointCount + second.PointCount) / (double)totalPointsToMerge,7:N3}"); lock (parts) { parts.Add(merged); } config.Storage.Add(merged.Id, merged); if (config.Verbose) { Console.WriteLine($"[MapReduce] merged " + $"{formatCell(first.Root.Value.Cell)} + {formatCell(second.Root.Value.Cell)} -> {formatCell(merged.Root.Value.Cell)} " + $"({first.Root.Value.PointCountTree:N0} + {second.Root.Value.PointCountTree:N0} -> {merged.Root.Value.PointCountTree:N0})" ); } if (merged.Root.Value.PointCountTree == 0) { throw new InvalidOperationException(); } return(merged); }, config.MaxDegreeOfParallelism ); if (config.Verbose) { Console.WriteLine($"[MapReduce] everything merged"); } config.CancellationToken.ThrowIfCancellationRequested(); #endregion config.Storage.Add(config.Key, final); config.ProgressCallback(1.0); return(final); string formatCell(Cell c) => c.IsCenteredAtOrigin ? $"[centered, {c.Exponent}]" : c.ToString(); }
/// <summary> /// Max tree depth. /// </summary> public static int CountOctreeLevels(this PointSet self) => CountOctreeLevels(self.Root.Value);
/// <summary> /// Returns lod points for given octree depth/front of cells intersecting given bounds, where level 0 is the root node. /// Front will include leafs higher up than given level. /// </summary> public static IEnumerable <Chunk> QueryPointsInOctreeLevel( this PointSet self, int level, Box3d bounds ) => QueryPointsInOctreeLevel(self.Root.Value, level, bounds);
/// <summary> /// Gets approximate number of points at given octree level within given bounds. /// For cells that only partially overlap the specified bounds all points are counted anyway. /// For performance reasons, in order to avoid per-point bounds checks. /// </summary> public static long CountPointsInOctreeLevel( this PointSet self, int level, Box3d bounds ) => CountPointsInOctreeLevel(self.Root.Value, level, bounds);
/// <summary> /// All points NOT within maxDistance of ALL the given planes. /// </summary> public static IEnumerable <Chunk> QueryPointsNotNearPlanes( this PointSet self, Plane3d[] planes, double maxDistance, int minCellExponent = int.MinValue ) => QueryPointsNotNearPlanes(self.Root.Value, planes, maxDistance, minCellExponent);
/// <summary> /// Count points approximately NOT within maxDistance of ALL the given polygons. /// Result is always equal or greater than exact number. /// Faster than CountPointsNotNearPolygons. /// </summary> public static long CountPointsApproximatelyNotNearPolygons( this PointSet self, Polygon3d[] polygons, double maxDistance, int minCellExponent = int.MinValue ) => CountPointsApproximatelyNotNearPolygons(self.Root.Value, polygons, maxDistance, minCellExponent);
/// <summary> /// All points outside convex hull (excluding boundary). /// </summary> public static IEnumerable <Chunk> QueryPointsOutsideConvexHull( this PointSet self, Hull3d query, int minCellExponent = int.MinValue ) => QueryPointsOutsideConvexHull(self.Root.Value, query, minCellExponent);
/// <summary> /// All points NOT within maxDistance of given polygon. /// </summary> public static IEnumerable <Chunk> QueryPointsNotNearPolygon( this PointSet self, Polygon3d polygon, double maxDistance, int minCellExponent = int.MinValue ) => QueryPointsNotNearPolygon(self.Root.Value, polygon, maxDistance, minCellExponent);
/// <summary> /// Count points within maxDistance of ANY of the given planes. /// </summary> public static long CountPointsNearPlanes( this PointSet self, Plane3d[] planes, double maxDistance, int minCellExponent = int.MinValue ) => CountPointsNearPlanes(self.Root.Value, planes, maxDistance, minCellExponent);
/// <summary> /// Maps a sequence of point chunks to point sets, which are then reduced to one single point set. /// </summary> public static PointSet MapReduce(this IEnumerable <Chunk> chunks, ImportConfig config) { var totalChunkCount = 0; var totalPointCountInChunks = 0L; Action <double> progress = x => config.ProgressCallback(x * 0.5); #region MAP: create one PointSet for each chunk var pointsets = chunks .MapParallel((chunk, ct2) => { Interlocked.Add(ref totalPointCountInChunks, chunk.Count); progress(1.0 - 1.0 / Interlocked.Increment(ref totalChunkCount)); var builder = InMemoryPointSet.Build(chunk, config.OctreeSplitLimit); var root = builder.ToPointSetCell(config.Storage, ct: ct2); var id = $"Aardvark.Geometry.PointSet.{Guid.NewGuid()}.json"; var pointSet = new PointSet(config.Storage, id, root.Id, config.OctreeSplitLimit); return(pointSet); }, config.MaxDegreeOfParallelism, null, config.CancellationToken ) .ToList() ; ; if (config.Verbose) { Console.WriteLine($"[MapReduce] pointsets : {pointsets.Count}"); Console.WriteLine($"[MapReduce] totalPointCountInChunks: {totalPointCountInChunks}"); } #endregion #region REDUCE: pairwise octree merge until a single (final) octree remains progress = x => config.ProgressCallback(0.5 + x * 0.5); var i = 0; var totalPointsToMerge = pointsets.Sum(x => x.PointCount); if (config.Verbose) { Console.WriteLine($"[MapReduce] totalPointsToMerge: {totalPointsToMerge}"); } var totalPointSetsCount = pointsets.Count; if (totalPointSetsCount == 0) { var empty = new PointSet(config.Storage, config.Key ?? Guid.NewGuid().ToString()); config.Storage.Add(config.Key, empty, config.CancellationToken); return(empty); } var final = pointsets.MapReduceParallel((first, second, ct2) => { progress(Interlocked.Increment(ref i) / (double)totalPointSetsCount); var merged = first.Merge(second, ct2); config.Storage.Add(merged.Id, merged, ct2); if (config.Verbose) { Console.WriteLine($"[MapReduce] merged " + $"{first.Root.Value.Cell} + {second.Root.Value.Cell} -> {merged.Root.Value.Cell} " + $"({first.Root.Value.PointCountTree} + {second.Root.Value.PointCountTree} -> {merged.Root.Value.PointCountTree})" ); } if (merged.Root.Value.PointCountTree == 0) { throw new InvalidOperationException(); } return(merged); }, config.MaxDegreeOfParallelism ); if (config.Verbose) { Console.WriteLine($"[MapReduce] everything merged"); } config.CancellationToken.ThrowIfCancellationRequested(); #endregion config.Storage.Add(config.Key, final, config.CancellationToken); config.ProgressCallback(1.0); return(final); }