Exemple #1
0
        /// <summary>
        /// Interpolates a single spot height from the design, using the optimized spatial index
        /// </summary>
        public override bool InterpolateHeight(ref int Hint,
                                               double X, double Y,
                                               double Offset,
                                               out double Z)
        {
            if (Hint != -1)
            {
                Z = GetHeight2(ref triangleItems[Hint], X, Y);
                if (Z != Common.Consts.NullDouble)
                {
                    Z += Offset;
                    return(true);
                }

                Hint = -1;
            }

            // Search in the sub grid triangle list for this sub grid from the spatial index

            SpatialIndexOptimised.CalculateIndexOfCellContainingPosition(X, Y, out int CellX, out int CellY);

            TriangleArrayReference arrayReference = SpatialIndexOptimised[CellX, CellY];

            if (arrayReference.Count == 0)
            {
                // There are no triangles that can satisfy the query
                Z = Common.Consts.NullReal;
                return(false);
            }

            // Search the triangles in the leaf to locate the one to interpolate height from
            int limit = arrayReference.TriangleArrayIndex + arrayReference.Count;

            for (int i = arrayReference.TriangleArrayIndex; i < limit; i++)
            {
                int triIndex = SpatialIndexOptimisedTriangles[i];
                Z = GetHeight2(ref triangleItems[triIndex], X, Y);

                if (Z != Common.Consts.NullReal)
                {
                    Hint = triIndex;
                    Z   += Offset;
                    return(true);
                }
            }

            Z = Common.Consts.NullReal;
            return(false);
        }
Exemple #2
0
        /// <summary>
        /// Build a spatial index for the triangles in the TIN surface by assigning each triangle to every sub grid it intersects with
        /// </summary>
        /// <returns></returns>
        public bool ConstructSpatialIndex()
        {
            // Read through all the triangles in the model and, for each triangle,
            // determine which sub grids in the index intersect it and add it to those sub grids
            try
            {
                // Create the optimized sub grid tree spatial index that minimizes the number of allocations in the final result.
                SpatialIndexOptimised = new OptimisedSpatialIndexSubGridTree(SubGridTreeConsts.SubGridTreeLevels - 1, SubGridTreeConsts.SubGridTreeDimension * CellSize);

                var FSpatialIndex = new NonOptimisedSpatialIndexSubGridTree(SubGridTreeConsts.SubGridTreeLevels - 1, SubGridTreeConsts.SubGridTreeDimension * CellSize);

                Log.LogInformation($"In: Constructing subgrid index for design containing {TTM.Triangles.Items.Length} triangles");
                try
                {
                    var cellScanner = new TriangleCellScanner(TTM);

                    // Construct a sub grid tree containing list of triangles that intersect each on-the-ground sub grid
                    int triangleCount = TTM.Triangles.Items.Length;
                    for (int triIndex = 0; triIndex < triangleCount; triIndex++)
                    {
                        cellScanner.ScanCellsOverTriangle(FSpatialIndex,
                                                          triIndex,
                                                          (tree, x, y) => false,
                                                          (tree, x, y, t) => IncludeTriangleInSubGridTreeIndex(tree as NonOptimisedSpatialIndexSubGridTree, x, y, t),
                                                          cellScanner.AddTrianglePieceToSubgridIndex);
                    }


                    if (EnableDuplicateRemoval)
                    {
                        /////////////////////////////////////////////////
                        // Remove duplicate triangles added to the lists
                        /////////////////////////////////////////////////
                        BitArray uniques         = new BitArray(TriangleItems.Length);
                        long     TotalDuplicates = 0;

                        FSpatialIndex.ScanAllSubGrids(leaf =>
                        {
                            // Iterate across all cells in each (level 5) leaf sub grid. Each cell represents
                            // a sub grid in the level 6 sub grid representing cells sampled across the surface at the
                            // core cell size for the project
                            SubGridUtilities.SubGridDimensionalIterator((x, y) =>
                            {
                                List <int> triList = FSpatialIndex[leaf.OriginX + x, leaf.OriginY + y];

                                if (triList == null)
                                {
                                    return;
                                }

                                uniques.SetAll(false);

                                int triListCount = triList.Count;
                                int uniqueCount  = 0;
                                for (int i = 0; i < triListCount; i++)
                                {
                                    int triIndex = triList[i];
                                    if (!uniques[triIndex])
                                    {
                                        triList[uniqueCount++] = triIndex;
                                        uniques[triIndex]      = true;
                                    }
                                    else
                                    {
                                        TotalDuplicates++;
                                    }
                                }

                                if (uniqueCount < triListCount)
                                {
                                    triList.RemoveRange(uniqueCount, triListCount - uniqueCount);
                                }
                            });

                            return(true);
                        });

                        Console.WriteLine($"Total duplicates encountered: {TotalDuplicates}");
                    }

                    // Transform this sub grid tree into one where each on-the-ground sub grid is represented by an index and a number of triangles present in a
                    // a single list of triangles.

                    // Count the number of triangle references present in the tree
                    var numTriangleReferences = 0;
                    FSpatialIndex.ForEach(x =>
                    {
                        numTriangleReferences += x?.Count ?? 0;
                        return(true);
                    });

                    // Create the single array
                    spatialIndexOptimisedTriangles = new int[numTriangleReferences];

                    /////////////////////////////////////////////////
                    // Iterate across all leaf sub grids
                    // Copy all triangle lists into it, and add the appropriate reference blocks in the new tree.
                    /////////////////////////////////////////////////

                    var copiedCount = 0;

                    var arrayReference = new TriangleArrayReference
                    {
                        Count = 0,
                        TriangleArrayIndex = 0
                    };

                    var cellWorldExtent = new BoundingWorldExtent3D();

                    FSpatialIndex.ScanAllSubGrids(leaf =>
                    {
                        // Iterate across all cells in each (level 5) leaf sub grid. Each cell represents
                        // a sub grid in the level 6 sub grid representing cells sampled across the surface at the
                        // core cell size for the project
                        SubGridUtilities.SubGridDimensionalIterator((x, y) =>
                        {
                            var CellX = leaf.OriginX + x;
                            var CellY = leaf.OriginY + y;

                            var triList = FSpatialIndex[CellX, CellY];

                            if (triList == null)
                            {
                                return;
                            }

                            /////////////////////////////////////////////////////////////////////////////////////////////////
                            // Start: Determine the triangles that definitely cannot cover one or more cells in each sub grid

                            var leafCellSize     = SpatialIndexOptimised.CellSize / SubGridTreeConsts.SubGridTreeDimension;
                            var halfLeafCellSize = leafCellSize / 2;

                            short trianglesCopiedToLeaf = 0;

                            SpatialIndexOptimised.GetCellExtents(CellX, CellY, ref cellWorldExtent);

                            // Compute the bounding structs for the triangles in this sub grid and remove any triangles whose
                            // bounding struct is null (ie: no cell centers are covered by its bounding box).

                            for (var i = 0; i < triList.Count; i++)
                            {
                                /* *** DO NOT REMOVE THIS CODE ***
                                 * It is commented out for now as it is an optimization for grid queries but it creates problems for profile queries
                                 * as in triangles that should be included for design profiling are excluded because they have no cell center in them.
                                 *
                                 * // Get the triangle...
                                 * var tri = TriangleItems[triList[i]];
                                 *
                                 * // Get the real world bounding box for the triangle
                                 *
                                 * var Vertex0 = VertexItems[tri.Vertex0];
                                 * var Vertex1 = VertexItems[tri.Vertex1];
                                 * var Vertex2 = VertexItems[tri.Vertex2];
                                 *
                                 * var TriangleWorldExtent_MinX = Math.Min(Vertex0.X, Math.Min(Vertex1.X, Vertex2.X));
                                 * var TriangleWorldExtent_MinY = Math.Min(Vertex0.Y, Math.Min(Vertex1.Y, Vertex2.Y));
                                 * var TriangleWorldExtent_MaxX = Math.Max(Vertex0.X, Math.Max(Vertex1.X, Vertex2.X));
                                 * var TriangleWorldExtent_MaxY = Math.Max(Vertex0.Y, Math.Max(Vertex1.Y, Vertex2.Y));
                                 *
                                 * // Calculate cell coordinates relative to the origin of the sub grid
                                 * var minCellX = (int)Math.Floor((TriangleWorldExtent_MinX - cellWorldExtent.MinX) / leafCellSize);
                                 * var minCellY = (int)Math.Floor((TriangleWorldExtent_MinY - cellWorldExtent.MinY) / leafCellSize);
                                 * var maxCellX = (int)Math.Floor((TriangleWorldExtent_MaxX - cellWorldExtent.MinX) / leafCellSize);
                                 * var maxCellY = (int)Math.Floor((TriangleWorldExtent_MaxY - cellWorldExtent.MinY) / leafCellSize);
                                 *
                                 * // Check if the result bounds are valid - if not, there is no point including it
                                 * if (minCellX > maxCellX || minCellY > maxCellY)
                                 * {
                                 * // There are no cell probe positions that can lie in this triangle, ignore it
                                 * continue;
                                 * }
                                 *
                                 * // Check if there is an intersection between the triangle cell bounds and the leaf cell bounds
                                 * if (minCellX > SubGridTreeConsts.SubGridTreeDimensionMinus1 || minCellY > SubGridTreeConsts.SubGridTreeDimensionMinus1 || maxCellX < 0 || maxCellY < 0)
                                 * {
                                 * // There is no bounding box intersection, ignore it
                                 * continue;
                                 * }
                                 *
                                 * // Transform the cell bounds by clamping them to the bounds of this sub grid
                                 * minCellX = minCellX <= 0 ? 0 : minCellX >= SubGridTreeConsts.SubGridTreeDimensionMinus1 ? SubGridTreeConsts.SubGridTreeDimensionMinus1 : minCellX;
                                 * minCellY = minCellY <= 0 ? 0 : minCellY >= SubGridTreeConsts.SubGridTreeDimensionMinus1 ? SubGridTreeConsts.SubGridTreeDimensionMinus1 : minCellY;
                                 * maxCellX = maxCellX <= 0 ? 0 : maxCellX >= SubGridTreeConsts.SubGridTreeDimensionMinus1 ? SubGridTreeConsts.SubGridTreeDimensionMinus1 : maxCellX;
                                 * maxCellY = maxCellY <= 0 ? 0 : maxCellY >= SubGridTreeConsts.SubGridTreeDimensionMinus1 ? SubGridTreeConsts.SubGridTreeDimensionMinus1 : maxCellY;
                                 *
                                 * // Check all the cells in the sub grid covered by this bounding box to check if at least one cell will actively probe this triangle
                                 *
                                 * var found = false;
                                 * var _x = cellWorldExtent.MinX + minCellX * leafCellSize + halfLeafCellSize;
                                 *
                                 * for (var cellX = minCellX; cellX <= maxCellX; cellX++)
                                 * {
                                 * var _y = cellWorldExtent.MinY + minCellY * leafCellSize + halfLeafCellSize;
                                 * for (var cellY = minCellY; cellY <= maxCellY; cellY++)
                                 * {
                                 *  if (XYZ.GetTriangleHeight(Vertex0, Vertex1, Vertex2, _x, _y) != Common.Consts.NullDouble)
                                 *  {
                                 *    found = true;
                                 *    break;
                                 *  }
                                 *
                                 *  _y += leafCellSize;
                                 * }
                                 *
                                 * if (found)
                                 *  break;
                                 *
                                 * _x += leafCellSize;
                                 * }
                                 *
                                 * if (!found)
                                 * {
                                 * // No cell in the sub grid intersects with the triangle - ignore it
                                 * continue;
                                 * }
                                 */
                                // This triangle is a candidate for being probed, copy it into the array
                                trianglesCopiedToLeaf++;
                                spatialIndexOptimisedTriangles[copiedCount++] = triList[i];
                            }
                            // End: Determine the triangles that definitely cannot cover one or more cells in each sub grid
                            ///////////////////////////////////////////////////////////////////////////////////////////////

                            arrayReference.Count = trianglesCopiedToLeaf;

                            // Add new entry for optimized tree
                            SpatialIndexOptimised[leaf.OriginX + x, leaf.OriginY + y] = arrayReference;

                            // Set copied count into the array reference for the next leaf so it captures the starting location in the overall array for it
                            arrayReference.TriangleArrayIndex = copiedCount;
                        });

                        return(true);
                    });

                    Console.WriteLine($"Number of vertices in model {VertexItems.Length}");
                    Console.WriteLine($"Number of triangles in model {TriangleItems.Length}");
                    Console.WriteLine($"Number of original triangle references in index: {spatialIndexOptimisedTriangles.Length}");
                    Console.WriteLine($"Number of triangle references removed as un-probable: {spatialIndexOptimisedTriangles.Length - copiedCount}");

                    // Finally, resize the master triangle reference array to remove the unused entries due to un-probable triangles
                    Array.Resize(ref spatialIndexOptimisedTriangles, copiedCount);

                    Console.WriteLine($"Final number of triangle references in index: {spatialIndexOptimisedTriangles.Length}");
                }
                finally
                {
                    // Emit some logging indicating likely efficiency of index.
                    long sumTriangleReferences = 0;
                    long sumTriangleLists      = 0;
                    long sumLeafSubGrids       = 0;
                    long sumNodeSubGrids       = 0;

                    FSpatialIndex.ScanAllSubGrids(l =>
                    {
                        sumLeafSubGrids++;
                        return(true);
                    },
                                                  n =>
                    {
                        sumNodeSubGrids++;
                        return(SubGridProcessNodeSubGridResult.OK);
                    });

                    FSpatialIndex.ForEach(x =>
                    {
                        sumTriangleLists++;
                        sumTriangleReferences += x?.Count ?? 0;
                        return(true);
                    });

                    Log.LogInformation(
                        $"Constructed sub grid index for design containing {TTM.Triangles.Items.Length} triangles, using {sumLeafSubGrids} leaf and {sumNodeSubGrids} node subgrids, {sumTriangleLists} triangle lists and {sumTriangleReferences} triangle references");
                }

                return(true);
            }
            catch (Exception e)
            {
                Log.LogError(e, "Exception in ConstructSpatialIndex");
                return(false);
            }
        }
Exemple #3
0
        /// <summary>
        /// Interpolates heights from the design for all the cells in a sub grid
        /// </summary>
        public override bool InterpolateHeights(float[,] Patch, double OriginX, double OriginY, double CellSize, double Offset)
        {
            bool hasValues = false;
            TriangleSubGridCellExtents triangleCellExtent = new TriangleSubGridCellExtents();

            double HalfCellSize             = CellSize / 2;
            double halfCellSizeMinusEpsilon = HalfCellSize - 0.0001;
            double OriginXPlusHalfCellSize  = OriginX + HalfCellSize;
            double OriginYPlusHalfCellSize  = OriginY + HalfCellSize;

            // Search in the sub grid triangle list for this sub grid from the spatial index
            // All cells in this sub grid will be contained in the same triangle list from the spatial index
            SpatialIndexOptimised.CalculateIndexOfCellContainingPosition(OriginXPlusHalfCellSize, OriginYPlusHalfCellSize, out int CellX, out int CellY);
            TriangleArrayReference arrayReference = SpatialIndexOptimised[CellX, CellY];
            int triangleCount = arrayReference.Count;

            if (triangleCount > 0) // There are triangles that can satisfy the query (leaf cell is non-empty)
            {
                double leafCellSize = SpatialIndexOptimised.CellSize / SubGridTreeConsts.SubGridTreeDimension;
                BoundingWorldExtent3D cellWorldExtent = SpatialIndexOptimised.GetCellExtents(CellX, CellY);

                // Create the array of triangle cell extents in the sub grid
                TriangleSubGridCellExtents[] triangleCellExtents = new TriangleSubGridCellExtents[triangleCount];

                // Compute the bounding structs for the triangles in this sub grid
                for (int i = 0; i < triangleCount; i++)
                {
                    // Get the triangle...
                    Triangle tri = triangleItems[SpatialIndexOptimisedTriangles[arrayReference.TriangleArrayIndex + i]];

                    // Get the real world bounding box for the triangle
                    // Note: As sampling occurs at cell centers shrink the effective bounding box for each triangle used
                    // for calculating the cell bounding box by half a cell size (less a small Epsilon) so the cell bounding box
                    // captures cell centers falling in the triangle world coordinate bounding box

                    XYZ TriVertex0 = vertexItems[tri.Vertex0];
                    XYZ TriVertex1 = vertexItems[tri.Vertex1];
                    XYZ TriVertex2 = vertexItems[tri.Vertex2];

                    double TriangleWorldExtent_MinX = Math.Min(TriVertex0.X, Math.Min(TriVertex1.X, TriVertex2.X)) + halfCellSizeMinusEpsilon;
                    double TriangleWorldExtent_MinY = Math.Min(TriVertex0.Y, Math.Min(TriVertex1.Y, TriVertex2.Y)) + halfCellSizeMinusEpsilon;
                    double TriangleWorldExtent_MaxX = Math.Max(TriVertex0.X, Math.Max(TriVertex1.X, TriVertex2.X)) - halfCellSizeMinusEpsilon;
                    double TriangleWorldExtent_MaxY = Math.Max(TriVertex0.Y, Math.Max(TriVertex1.Y, TriVertex2.Y)) - halfCellSizeMinusEpsilon;

                    int minCellX = (int)Math.Floor((TriangleWorldExtent_MinX - cellWorldExtent.MinX) / leafCellSize);
                    int minCellY = (int)Math.Floor((TriangleWorldExtent_MinY - cellWorldExtent.MinY) / leafCellSize);
                    int maxCellX = (int)Math.Floor((TriangleWorldExtent_MaxX - cellWorldExtent.MinX) / leafCellSize);
                    int maxCellY = (int)Math.Floor((TriangleWorldExtent_MaxY - cellWorldExtent.MinY) / leafCellSize);

                    triangleCellExtent.MinX = (byte)(minCellX <= 0 ? 0 : minCellX >= SubGridTreeConsts.SubGridTreeDimensionMinus1 ? SubGridTreeConsts.SubGridTreeDimensionMinus1 : minCellX);
                    triangleCellExtent.MinY = (byte)(minCellY <= 0 ? 0 : minCellY >= SubGridTreeConsts.SubGridTreeDimensionMinus1 ? SubGridTreeConsts.SubGridTreeDimensionMinus1 : minCellY);
                    triangleCellExtent.MaxX = (byte)(maxCellX <= 0 ? 0 : maxCellX >= SubGridTreeConsts.SubGridTreeDimensionMinus1 ? SubGridTreeConsts.SubGridTreeDimensionMinus1 : maxCellX);
                    triangleCellExtent.MaxY = (byte)(maxCellY <= 0 ? 0 : maxCellY >= SubGridTreeConsts.SubGridTreeDimensionMinus1 ? SubGridTreeConsts.SubGridTreeDimensionMinus1 : maxCellY);

                    triangleCellExtents[i] = triangleCellExtent;
                }

                // Initialise patch to null height values
                Array.Copy(kNullPatch, 0, Patch, 0, SubGridTreeConsts.SubGridTreeCellsPerSubGrid);

                // Iterate over all the cells in the grid using the triangle sub grid cell extents to filter
                // triangles in the leaf that will be considered for point-in-triangle & elevation checks.

                double X = OriginXPlusHalfCellSize;
                for (int x = 0; x < SubGridTreeConsts.SubGridTreeDimension; x++)
                {
                    double Y = OriginYPlusHalfCellSize;
                    for (int y = 0; y < SubGridTreeConsts.SubGridTreeDimension; y++)
                    {
                        // Search the triangles in the leaf to locate the one to interpolate height from
                        for (int i = 0; i < triangleCount; i++)
                        {
                            if (x < triangleCellExtents[i].MinX || x > triangleCellExtents[i].MaxX || y <triangleCellExtents[i].MinY || y> triangleCellExtents[i].MaxY)
                            {
                                continue; // No intersection, move to next triangle
                            }
                            var Z = GetHeight2(ref triangleItems[SpatialIndexOptimisedTriangles[arrayReference.TriangleArrayIndex + i]], X, Y);

                            if (Z != Common.Consts.NullReal)
                            {
                                hasValues   = true;
                                Patch[x, y] = (float)(Z + Offset);

                                break; // No more triangles need to be examined for this cell
                            }
                        }

                        Y += CellSize;
                    }

                    X += CellSize;
                }
            }

            return(hasValues);
        }