/// <summary>
 /// Counts points outside convex hull (approximately).
 /// Result is always equal or greater than exact number.
 /// </summary>
 internal static long CountPointsApproximatelyOutsideConvexHull(
     this IPointCloudNode self, Hull3d query, int minCellExponent = int.MinValue
     )
 => CountPointsApproximately(self,
                             n => !query.Intersects(n.BoundingBoxExactGlobal),
                             n => query.Contains(n.BoundingBoxExactGlobal),
                             minCellExponent);
示例#2
0
        /// <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 IPointCloudNode node, int level, Box3d bounds
            )
        {
            if (level < 0)
            {
                return(0);
            }
            if (!node.BoundingBoxExactGlobal.Intersects(bounds))
            {
                return(0);
            }

            if (level == 0 || node.IsLeaf())
            {
                return(node.Positions.Value.Length);
            }
            else
            {
                var nextLevel = level - 1;
                var sum       = 0L;
                for (var i = 0; i < 8; i++)
                {
                    var n = node.Subnodes[i];
                    if (n == null)
                    {
                        continue;
                    }
                    sum += CountPointsInOctreeLevel(n.Value, nextLevel, bounds);
                }
                return(sum);
            }
        }
示例#3
0
 /// <summary>
 /// Counts points approximately inside axis-aligned box (cell granularity).
 /// Result is always equal or greater than exact number.
 /// Faster than CountPointsInsideBox.
 /// </summary>
 public static long CountPointsApproximatelyInsideBox(
     this IPointCloudNode self, Box3d query, int minCellExponent = int.MinValue
     )
 => CountPointsApproximately(self,
                             n => query.Contains(n.BoundingBoxExactGlobal),
                             n => !query.Intersects(n.BoundingBoxExactGlobal),
                             minCellExponent);
        /// <summary>
        /// </summary>
        public static IEnumerable <IPointCloudNode> ForEachNode(
            this IPointCloudNode self, int minCellExponent = int.MinValue
            )
        {
            if (self == null)
            {
                yield break;
            }

            if (self.Cell.Exponent < minCellExponent)
            {
                yield break;
            }

            yield return(self);

            if (self.Subnodes != null)
            {
                for (var i = 0; i < 8; i++)
                {
                    var n = self.Subnodes[i];
                    if (n == null)
                    {
                        continue;
                    }
                    foreach (var x in ForEachNode(n.Value, minCellExponent))
                    {
                        yield return(x);
                    }
                }
            }
        }
        /// <summary>
        /// Calls action for each node in this tree.
        /// </summary>
        public static void ForEachNode(
            this IPointCloudNode self, bool outOfCore, Action <IPointCloudNode> action)
        {
            action(self);

            if (self.Subnodes == null)
            {
                return;
            }

            if (outOfCore)
            {
                for (var i = 0; i < 8; i++)
                {
                    self.Subnodes[i]?.Value.ForEachNode(outOfCore, action);
                }
            }
            else
            {
                for (var i = 0; i < 8; i++)
                {
                    var n = self.Subnodes[i];
                    if (n != null)
                    {
                        if (n.TryGetValue(out IPointCloudNode node))
                        {
                            node.ForEachNode(outOfCore, action);
                        }
                    }
                }
            }
        }
示例#6
0
        /// <summary>
        /// Gets total number of lod-points in all cells at given octree level.
        /// </summary>
        public static long CountPointsInOctreeLevel(
            this IPointCloudNode node, int level
            )
        {
            if (level < 0)
            {
                return(0);
            }

            if (level == 0 || node.IsLeaf())
            {
                return(node.Positions.Value.Count());
            }
            else
            {
                var nextLevel = level - 1;
                var sum       = 0L;
                for (var i = 0; i < 8; i++)
                {
                    var n = node.Subnodes[i];
                    if (n == null)
                    {
                        continue;
                    }
                    sum += CountPointsInOctreeLevel(n.Value, nextLevel);
                }
                return(sum);
            }
        }
示例#7
0
        public static void ValidateTree(this IPointCloudNode node, int splitLimit, bool hasLod = true)
        {
            if (node != null)
            {
                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.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.IsLeaf)
                {
                    var ps = node.PositionsAbsolute;
                    Assert.IsTrue(node.PointCountCell == node.PointCountTree);
                    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 == nodes.Sum(n => n != null ? n.PointCountTree : 0));

                    foreach (var n in nodes)
                    {
                        ValidateTree(n, splitLimit, hasLod);
                    }
                }
            }
        }
        /// <summary>
        /// Calls action for each (node, fullyInside) in this tree that is intersecting the given hull.
        /// </summary>
        public static IEnumerable <CellQueryResult> ForEachNodeIntersecting(
            this IPointCloudNode self,
            Hull3d hull, bool doNotTraverseSubnodesWhenFullyInside, int minCellExponent = int.MinValue
            )
        {
            if (self == null)
            {
                yield break;
            }

            if (self.Cell.Exponent < minCellExponent)
            {
                yield break;
            }

            for (var i = 0; i < hull.PlaneCount; i++)
            {
                if (!self.IntersectsNegativeHalfSpace(hull.PlaneArray[i]))
                {
                    yield break;
                }
            }

            bool fullyInside = true;

            for (var i = 0; i < hull.PlaneCount; i++)
            {
                if (!self.InsideNegativeHalfSpace(hull.PlaneArray[i]))
                {
                    fullyInside = false;
                    break;
                }
            }

            yield return(new CellQueryResult(self, fullyInside));

            if (fullyInside && doNotTraverseSubnodesWhenFullyInside)
            {
                yield break;
            }

            if (self.Subnodes == null)
            {
                yield break;
            }
            for (var i = 0; i < 8; i++)
            {
                var n = self.Subnodes[i];
                if (n == null)
                {
                    continue;
                }
                var xs = ForEachNodeIntersecting(n.Value, hull, doNotTraverseSubnodesWhenFullyInside, minCellExponent);
                foreach (var x in xs)
                {
                    yield return(x);
                }
            }
        }
示例#9
0
 /// <summary>
 /// Count points approximately NOT within maxDistance of ALL the given planes.
 /// Result is always equal or greater than exact number.
 /// Faster than CountPointsNotNearPlanes.
 /// </summary>
 public static long CountPointsApproximatelyNotNearPlanes(
     this IPointCloudNode node, Plane3d[] planes, double maxDistance, int minCellExponent = int.MinValue
     )
 => CountPointsApproximately(node,
                             n => !planes.Any(plane => node.BoundingBoxExactGlobal.Intersects(plane, maxDistance)),
                             n => planes.Any(plane => plane.Contains(maxDistance, node.BoundingBoxExactGlobal)),
                             minCellExponent
                             );
 /// <summary>
 /// All points inside convex hull (including boundary).
 /// </summary>
 public static IEnumerable <Chunk> QueryPointsInsideConvexHull(
     this IPointCloudNode self, Hull3d query, int minCellExponent = int.MinValue
     )
 => QueryPoints(self,
                n => query.Contains(n.BoundingBoxExactGlobal),
                n => !query.Intersects(n.BoundingBoxExactGlobal),
                p => query.Contains(p),
                minCellExponent);
示例#11
0
 /// <summary>
 /// Count points within maxDistance of ANY of the given planes.
 /// </summary>
 public static long CountPointsNearPlanes(
     this IPointCloudNode node, Plane3d[] planes, double maxDistance, int minCellExponent = int.MinValue
     )
 => CountPoints(node,
                n => planes.Any(plane => plane.Contains(maxDistance, node.BoundingBoxExactGlobal)),
                n => !planes.Any(plane => node.BoundingBoxExactGlobal.Intersects(plane, maxDistance)),
                p => planes.Any(plane => Math.Abs(plane.Height(p)) <= maxDistance),
                minCellExponent
                );
示例#12
0
 /// <summary>
 /// All points NOT within maxDistance of given plane.
 /// </summary>
 public static IEnumerable <Chunk> QueryPointsNotNearPlane(
     this IPointCloudNode node, Plane3d plane, double maxDistance, int minCellExponent = int.MinValue
     )
 => QueryPoints(node,
                n => !node.BoundingBoxExactGlobal.Intersects(plane, maxDistance),
                n => plane.Contains(maxDistance, node.BoundingBoxExactGlobal),
                p => Math.Abs(plane.Height(p)) > maxDistance,
                minCellExponent
                );
示例#13
0
 /// <summary>
 /// </summary>
 public static IPointCloudNode Create(Guid id, IPointCloudNode node, IFilter filter)
 {
     if (node.IsTemporaryImportNode)
     {
         throw new InvalidOperationException(
                   "FilteredNode cannot be created from temporary import node. Invariant b9c2dca3-1510-4ea7-959f-6a0737c707fa."
                   );
     }
     return(new FilteredNode(id, true, node, filter));
 }
        /// <summary></summary>
        public HashSet <int> FilterPoints(IPointCloudNode node, HashSet <int> selected = null)
        {
            var a = Left.FilterPoints(node, selected);
            var b = Right.FilterPoints(node, selected);

            if (selected != null && a.Count == selected.Count && b.Count == selected.Count)
            {
                return(selected);
            }
            a.IntersectWith(b);
            return(a);
        }
示例#15
0
 /// <summary>
 /// Max tree depth.
 /// </summary>
 public static int CountOctreeLevels(this IPointCloudNode root)
 {
     if (root == null)
     {
         return(0);
     }
     if (root.Subnodes == null)
     {
         return(1);
     }
     return(root.Subnodes.Select(n => CountOctreeLevels(n?.Value)).Max() + 1);
 }
        /// <summary>
        /// Returns points in given cell,
        /// or null if octree does not cover given cell.
        /// Result chunk contains 0 points, if cell is covered by octree, but no points are inside given cell.
        /// </summary>
        public static CellQueryResult QueryCell(this IPointCloudNode root, Cell cell)
        {
            if (root == null)
            {
                throw new ArgumentNullException(nameof(root));
            }

            if (!root.Cell.Contains(cell))
            {
                throw new InvalidOperationException(
                          $"Octree ({root.Cell}) must contain requested cell ({cell}). " +
                          "Invariant 4d67081e-37de-48d4-87df-5f5022f9051f."
                          );
            }

            return(QueryCellRecursive(root));

            CellQueryResult QueryCellRecursive(IPointCloudNode n)
            {
                if (n.Cell == cell)
                {
                    // found!
                    return(new CellQueryResult(root, cell, n));
                }
                else
                {
                    // continue search in subnode ...
                    var octant = n.Cell.GetOctant(cell);
                    if (octant.HasValue)
                    {
                        var subNodeRef = n.Subnodes[octant.Value];
                        if (subNodeRef != null)
                        {
                            return(QueryCellRecursive(subNodeRef.Value));
                        }
                        else
                        {
                            // we can't go deeper
                            return(new CellQueryResult(root, cell, n));
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException(
                                  $"Node ({n.Cell}) must contain requested cell ({cell}). " +
                                  "Invariant fbde10dd-1c07-41c8-9fc0-8a98b74d3948."
                                  );
                    }
                }
            }
        }
示例#17
0
        /// <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 IPointCloudNode node, Polygon3d[] polygons, double maxDistance, int minCellExponent = int.MinValue
            )
        {
            var bounds = polygons.Map(x => x.BoundingBox3d(maxDistance));
            var planes = polygons.Map(x => x.GetPlane3d());
            var w2p    = planes.Map(x => x.GetWorldToPlane());
            var poly2d = polygons.Map((x, i) => new Polygon2d(x.GetPointArray().Map(p => w2p[i].TransformPos(p).XY)));

            return(CountPointsApproximately(node,
                                            n => !bounds.Any(b => n.BoundingBoxExactGlobal.Intersects(b)),
                                            n => false,
                                            minCellExponent
                                            ));
        }
示例#18
0
        /// <summary>
        /// Count points approximately NOT within maxDistance of given polygon.
        /// Result is always equal or greater than exact number.
        /// Faster than CountPointsNotNearPolygon.
        /// </summary>
        public static long CountPointsApproximatelyNotNearPolygon(
            this IPointCloudNode node, Polygon3d polygon, double maxDistance, int minCellExponent = int.MinValue
            )
        {
            var bounds = polygon.BoundingBox3d(maxDistance);
            var plane  = polygon.GetPlane3d();
            var w2p    = plane.GetWorldToPlane();
            var poly2d = new Polygon2d(polygon.GetPointArray().Map(p => w2p.TransformPos(p).XY));

            return(CountPointsApproximately(node,
                                            n => !n.BoundingBoxExactGlobal.Intersects(bounds),
                                            n => false,
                                            minCellExponent
                                            ));
        }
示例#19
0
        /// <summary>
        /// Creates pointset from given root cell.
        /// </summary>
        public PointSet(Storage storage, string key, IPointCloudNode root, int splitLimit)
        {
            if (root == null)
            {
                throw new ArgumentNullException(nameof(root));
            }

            Storage    = storage;
            Id         = key ?? throw new ArgumentNullException(nameof(key));
            SplitLimit = splitLimit;

            if (key != null)
            {
                Root = new PersistentRef <IPointCloudNode>(root.Id.ToString(), storage.GetPointCloudNode, storage.TryGetPointCloudNode);
            }
        }
示例#20
0
        /// <summary>
        /// All points NOT within maxDistance of given polygon.
        /// </summary>
        public static IEnumerable <Chunk> QueryPointsNotNearPolygon(
            this IPointCloudNode node, Polygon3d polygon, double maxDistance, int minCellExponent = int.MinValue
            )
        {
            var bounds = polygon.BoundingBox3d(maxDistance);
            var plane  = polygon.GetPlane3d();
            var w2p    = plane.GetWorldToPlane();
            var poly2d = new Polygon2d(polygon.GetPointArray().Map(p => w2p.TransformPos(p).XY));

            return(QueryPoints(node,
                               n => !n.BoundingBoxExactGlobal.Intersects(bounds),
                               n => false,
                               p => !polygon.Contains(plane, w2p, poly2d, maxDistance, p, out double d),
                               minCellExponent
                               ));
        }
示例#21
0
        /// <summary>
        /// All points within maxDistance of ANY of the given polygons.
        /// </summary>
        public static IEnumerable <Chunk> QueryPointsNearPolygons(
            this IPointCloudNode node, Polygon3d[] polygons, double maxDistance, int minCellExponent = int.MinValue
            )
        {
            var bounds = polygons.Map(x => x.BoundingBox3d(maxDistance));
            var planes = polygons.Map(x => x.GetPlane3d());
            var w2p    = planes.Map(x => x.GetWorldToPlane());
            var poly2d = polygons.Map((x, i) => new Polygon2d(x.GetPointArray().Map(p => w2p[i].TransformPos(p).XY)));

            return(QueryPoints(node,
                               n => false,
                               n => !bounds.Any(b => n.BoundingBoxExactGlobal.Intersects(b)),
                               p => planes.Any((plane, i) => polygons[i].Contains(plane, w2p[i], poly2d[i], maxDistance, p, out double d)),
                               minCellExponent
                               ));
        }
示例#22
0
        /// <summary>
        /// Finds deepest octree level which still contains less than given number of points within given bounds.
        /// </summary>
        public static int GetMaxOctreeLevelWithLessThanGivenPointCount(
            this IPointCloudNode node, long maxPointCount, Box3d bounds
            )
        {
            var imax = node.CountOctreeLevels();

            for (var i = 0; i < imax; i++)
            {
                var count = node.CountPointsInOctreeLevel(i, bounds);
                if (count >= maxPointCount)
                {
                    return(i - 1);
                }
            }

            return(imax - 1);
        }
示例#23
0
        /// <summary>
        /// Returns lod points for given octree depth/front, where level 0 is the root node.
        /// Front will include leafs higher up than given level.
        /// </summary>
        public static IEnumerable <Chunk> QueryPointsInOctreeLevel(
            this IPointCloudNode node, int level
            )
        {
            if (level < 0)
            {
                yield break;
            }

            if (level == 0 || node.IsLeaf())
            {
                var ps = node.PositionsAbsolute;
                var cs = node?.TryGetColors4b()?.Value;
                if (ps != null && cs != null && ps.Length != cs.Length)
                {
                    cs = new C4b[ps.Length];
                    Report.Warn("[Chunk] inconsistent length: pos.length = {0} vs cs.length = {1}", ps.Length, cs.Length);
                }
                var ns    = node?.TryGetNormals3f()?.Value;
                var js    = node?.TryGetIntensities()?.Value;
                var ks    = node?.TryGetClassifications()?.Value;
                var chunk = new Chunk(ps, cs, ns, js, ks);
                yield return(chunk);
            }
            else
            {
                if (node.Subnodes == null)
                {
                    yield break;
                }

                for (var i = 0; i < 8; i++)
                {
                    var n = node.Subnodes[i];
                    if (n == null)
                    {
                        continue;
                    }
                    foreach (var x in QueryPointsInOctreeLevel(n.Value, level - 1))
                    {
                        yield return(x);
                    }
                }
            }
        }
示例#24
0
        /// <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 IPointCloudNode node, int level, Box3d bounds
            )
        {
            if (level < 0)
            {
                yield break;
            }
            if (!node.BoundingBoxExactGlobal.Intersects(bounds))
            {
                yield break;
            }

            if (level == 0 || node.IsLeaf())
            {
                var ps    = node.PositionsAbsolute;
                var cs    = node?.TryGetColors4b()?.Value;
                var ns    = node?.TryGetNormals3f()?.Value;
                var js    = node?.TryGetIntensities()?.Value;
                var ks    = node?.TryGetClassifications()?.Value;
                var chunk = new Chunk(ps, cs, ns, js, ks);
                yield return(chunk);
            }
            else
            {
                if (node.Subnodes == null)
                {
                    yield break;
                }

                for (var i = 0; i < 8; i++)
                {
                    var n = node.Subnodes[i];
                    if (n == null)
                    {
                        continue;
                    }
                    foreach (var x in QueryPointsInOctreeLevel(n.Value, level - 1, bounds))
                    {
                        yield return(x);
                    }
                }
            }
        }
示例#25
0
        /// <summary></summary>
        public bool IsFullyOutside(IPointCloudNode node)
        {
            var xs = GetValues(node);

            if (xs == null)
            {
                return(false);
            }

            for (var i = 0; i < xs.Length; i++)
            {
                if (Filter.Contains(xs[i]))
                {
                    return(false);
                }
            }

            return(true);
        }
            /// <summary>
            /// Represents a cell 'resultCell' inside an octree ('root'),
            /// where 'resultNode' is root's smallest subnode (incl. root) containing 'resultCell'.
            /// </summary>
            public CellQueryResult(IPointCloudNode root, Cell resultCell, IPointCloudNode resultNode)
            {
                Root     = root ?? throw new ArgumentNullException(nameof(root));
                Cell     = resultCell;
                m_result = resultNode;

                if (!root.Cell.Contains(resultNode.Cell))
                {
                    throw new Exception(
                              $"Root node {root.Cell} must contain resultNode {resultNode.Cell}. Invariant fb8dc278-fa35-4022-8aa8-281855dd41af."
                              );
                }

                if (resultNode != null && !resultNode.Cell.Contains(resultCell))
                {
                    throw new Exception(
                              $"Result node {resultNode.Cell} must contain resultCell {resultCell}. Invariant 62bff5cc-61b1-4cec-a9f8-b2e1136c19d1."
                              );
                }
            }
示例#27
0
        /// <summary></summary>
        public HashSet <int> FilterPoints(IPointCloudNode node, HashSet <int> selected = null)
        {
            var xs = GetValues(node);

            if (selected != null)
            {
                return(new HashSet <int>(selected.Where(i => V3f.Cross(Direction, xs[i].Normalized).LengthSquared <= m_eps)));
            }
            else
            {
                var result = new HashSet <int>();
                for (var i = 0; i < xs.Length; i++)
                {
                    if (V3f.Cross(Direction, xs[i].Normalized).LengthSquared <= m_eps)
                    {
                        result.Add(i);
                    }
                }
                return(result);
            }
        }
        /// <summary></summary>
        public HashSet <int> FilterPoints(IPointCloudNode node, HashSet <int> selected = null)
        {
            var xs = GetValues(node);

            if (selected != null)
            {
                return(new HashSet <int>(selected.Where(i => Range.Contains(xs[i]))));
            }
            else
            {
                var result = new HashSet <int>();
                for (var i = 0; i < xs.Length; i++)
                {
                    if (Range.Contains(xs[i]))
                    {
                        result.Add(i);
                    }
                }
                return(result);
            }
        }
示例#29
0
        /// <summary></summary>
        public HashSet <int> FilterPoints(IPointCloudNode node, HashSet <int> selected = null)
        {
            var c  = node.Center;
            var xs = node.Positions.Value;

            if (selected != null)
            {
                return(new HashSet <int>(selected.Where(i => Box.Contains(c + (V3d)xs[i]))));
            }
            else
            {
                var result = new HashSet <int>();
                for (var i = 0; i < xs.Length; i++)
                {
                    if (Box.Contains(c + (V3d)xs[i]))
                    {
                        result.Add(i);
                    }
                }
                return(result);
            }
        }
示例#30
0
 /// <summary></summary>
 private FilteredNode(Guid id, bool writeToStore, IPointCloudNode node, IFilter filter)
 {
     Id     = id;
     Node   = node ?? throw new ArgumentNullException(nameof(node));
     Filter = filter ?? throw new ArgumentNullException(nameof(filter));
     if (filter.IsFullyInside(node))
     {
         m_activePoints = null;
     }
     else if (filter.IsFullyOutside(node))
     {
         m_activePoints = new HashSet <int>();
     }
     else
     {
         m_activePoints = Filter.FilterPoints(node, m_activePoints);
     }
     if (writeToStore)
     {
         WriteToStore();
         //Console.WriteLine("Written to store: {0}", id);
     }
 }