示例#1
0
        /// <summary>
        /// Loads a sub grid spatial index for the design from a file
        /// </summary>
        private bool LoadSpatialIndexFile(string fileName, bool saveIndexFile)
        {
            Log.LogInformation($"Loading spatial index file {fileName}");

            bool Result = LoadSpatialIndex(fileName);

            if (!Result)
            {
                // Build the sub grid tree based spatial index
                var indexBuilder = new OptimisedTTMSpatialIndexBuilder(Data, cellSize);
                Result = indexBuilder.ConstructSpatialIndex();

                if (Result)
                {
                    SpatialIndexOptimised          = indexBuilder.SpatialIndexOptimised;
                    SpatialIndexOptimisedTriangles = indexBuilder.SpatialIndexOptimisedTriangles;

                    if (saveIndexFile && !SaveSpatialIndex(fileName))
                    {
                        Log.LogError("Continuing with unsaved index");
                    }
                }
                else
                {
                    Log.LogError($"Unable to create and save spatial index file {fileName}");
                }
            }

            return(Result);
        }
示例#2
0
        /// <summary>
        /// Constructor for a TTMDesign that takes the underlying cell size for the site model that will be used when interpolating heights from the design surface
        /// </summary>
        public TTMDesign(double ACellSize)
        {
            Data          = new TrimbleTINModel();
            triangleItems = Data.Triangles.Items;
            vertexItems   = Data.Vertices.Items;

            cellSize = ACellSize;

            // Create a sub grid tree bit mask index that holds one bit per on-the-ground
            // sub grid that intersects at least one triangle in the TTM.
            subGridIndex = new SubGridTreeSubGridExistenceBitMask {
                CellSize = SubGridTreeConsts.SubGridTreeDimension * ACellSize
            };

            // 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 * ACellSize);
        }
示例#3
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);
            }
        }