예제 #1
0
        [ExcludeFromCodeCoverage] // This method not currently used in favour of cell by cell lookups into triangles
        private void AddTrianglePieceToElevationPatch(XYZ H1, XYZ H2, XYZ V,
                                                      Triangle Tri,
                                                      bool SingleRowOnly,
                                                      double OriginX, double OriginY,
                                                      double CellSize,
                                                      float[,] Patch,
                                                      double OffSet,
                                                      ref int ValueCount)
        {
            double H1Slope, H2Slope;
            int    Delta;

            // H1 and H2 describe the horizontal portion of the triangle piece
            // V describes the vertex above, or below the horizontal line

            // Ensure H1 is left of H2 and take local copies of the vertex ordinates
            if (H1.X > H2.X)
            {
                DesignGeometry.SwapVertices(ref H1, ref H2);
            }

            double H1X = H1.X;
            double H1Y = H1.Y;
            double H2X = H2.X;
            double H2Y = H2.Y;
            double VX  = V.X;
            double VY  = V.Y;

            // HalfMinorCellSize is half of the cell size of the on-the-ground cells that
            // will be compared against the TIN design surface during cut fill operations.
            // As the sample point for a cell is the center point of the cell then there is
            // no need to include a half cell width outer boundary of each cell in the subgrid
            // index. A small epsilon value is deducted from the half cell size value to prevent
            // numeric imprecision
            double HalfCellSize      = CellSize / 2;
            double HalfMinorCellSize = HalfCellSize - 0.001;

            double PatchSize = SubGridTreeConsts.SubGridTreeDimension * CellSize;
            double TopEdge   = OriginY + PatchSize;
            double RightEdge = OriginX + PatchSize;

            double OriginXPlusHalfCell = OriginX + HalfMinorCellSize;
            double OriginYPlusHalfCell = OriginY + HalfMinorCellSize;

            double TopEdgeLessHalfCell   = TopEdge - HalfMinorCellSize;
            double RightEdgeLessHalfCell = RightEdge - HalfMinorCellSize;

            // Check to see if the triangle piece being considered could possibly intersect
            // the extent of the patch (or any of the cell center positions at which the
            // spot elevation are calculated).
            if (((H1X > RightEdgeLessHalfCell) && (VX > RightEdgeLessHalfCell)) ||
                ((H2X < OriginXPlusHalfCell) && (VX < OriginXPlusHalfCell)) ||
                ((H1Y > TopEdgeLessHalfCell) && (H2Y > TopEdgeLessHalfCell) && (VY > TopEdgeLessHalfCell)) ||
                ((H1Y < OriginYPlusHalfCell) && (H2Y < OriginYPlusHalfCell) && (VY < OriginYPlusHalfCell)))
            {
                // The triangle piece cannot intersect the patch
                return;
            }

            int PatchOriginCellIndexX = (int)Math.Floor(OriginXPlusHalfCell / CellSize);
            int PatchOriginCellIndexY = (int)Math.Floor(OriginYPlusHalfCell / CellSize);
            int PatchCellLimitIndexX  = PatchOriginCellIndexX + SubGridTreeConsts.SubGridTreeDimension - 1;

            // Work out 'Y' range and step direction of the triangle piece.
            double YRange = VY - H1Y;
            int    YStep  = Math.Sign(YRange);

            try
            {
                if (SingleRowOnly)
                {
                    H1Slope = 0;
                    H2Slope = 0;
                }
                else
                {
                    H1Slope = (VX - H1X) / Math.Abs(YRange);
                    H2Slope = (H2X - VX) / Math.Abs(YRange);
                }
            }
            catch
            {
                H1Slope = 0;
                H2Slope = 0;
            }

            double H1SlopeTimesCellSize = H1Slope * CellSize;
            double H2SlopeTimesCellSize = H2Slope * CellSize;

            double AbsH1SlopeTimesCellSize = Math.Abs(H1SlopeTimesCellSize) + 0.001;
            double AbsH2SlopeTimesCellSize = Math.Abs(H2SlopeTimesCellSize) + 0.001;

            // ProcessingCellYIndex is used to ensure that each 'row' of cells is adjacent to the
            // previous row to ensure a row of cells is not skipped in the event that
            // H1 and H2 vertices lie on the boundary of two cells which may cause numeric
            // imprecision when the H1 and H2 vertices are updated after scanning across the
            // cells in the row.

            int VCellIndexY = (int)Math.Floor(VY / CellSize);
            int HCellIndexY = (int)Math.Floor(H1Y / CellSize);

            int VCellPatchIndex = VCellIndexY - PatchOriginCellIndexY;
            int HCellPatchIndex = HCellIndexY - PatchOriginCellIndexY;

            int NumCellRowsToProcess = Math.Abs(VCellPatchIndex - HCellPatchIndex) + 1;

            int ProcessingCellYIndex = HCellPatchIndex;

            // Determine how many rows of cells there are between ProcessingCellYIndex and
            // the extent covered by the sub grid. Shift the H1X/H2X/etc values appropriately,
            // also clamping the starting cell row index to the patch
            if (HCellPatchIndex < 0)
            {
                if (YStep == -1) // There's nothing more to be done here
                {
                    return;
                }

                Delta = -HCellPatchIndex;
                H1X   = H1X + Delta * H1SlopeTimesCellSize;
                H2X   = H2X - Delta * H2SlopeTimesCellSize;

                NumCellRowsToProcess -= Delta;
                ProcessingCellYIndex  = 0;
            }
            else
            {
                if (HCellPatchIndex >= SubGridTreeConsts.SubGridTreeDimension)
                {
                    if (YStep == 1) // There's nothing more to be done here
                    {
                        return;
                    }

                    Delta = (HCellPatchIndex - SubGridTreeConsts.SubGridTreeDimension) + 1;
                    H1X   = H1X + Delta * H1SlopeTimesCellSize;
                    H2X   = H2X - Delta * H2SlopeTimesCellSize;

                    NumCellRowsToProcess -= Delta;
                    ProcessingCellYIndex  = SubGridTreeConsts.SubGridTreeDimension - 1;
                }
            }

            // Clamp the ending cell row to be processed to the patch
            if (VCellPatchIndex < 0)
            {
                if (YStep == 1)
                {
                    return; // Nothing more to do here
                }

                NumCellRowsToProcess -= -VCellPatchIndex;
            }
            else if (VCellPatchIndex >= SubGridTreeConsts.SubGridTreeDimension)
            {
                if (YStep == -1)
                {
                    return; // Nothing more to do here
                }

                NumCellRowsToProcess -= ((VCellPatchIndex - SubGridTreeConsts.SubGridTreeDimension) + 1);
            }

            if (NumCellRowsToProcess == 0)
            {
                return;
            }

            // Widen the H1/H2 spread to adequately cover the cells in the interval
            // as iterating across just this interval will leave cells on the extreme
            // edges missed out from the spot elevation calculations

            H1X -= AbsH1SlopeTimesCellSize;
            H2X += AbsH2SlopeTimesCellSize;

            // Note: H1X & H2X are modified in the loop after this location

            // Repeatedly scan over rows of cells that cover the triangle piece checking
            // if they cover the body of the triangle
            do //repeat
            {
                // Calculate the positions of the left and right cell indices in the coordinate space of the
                // triangle piece
                int LeftCellIndexX  = (int)Math.Floor(H1X / CellSize);
                int RightCellIndexX = (int)Math.Floor(H2X / CellSize) + 1;

                // Clip the calculated cell indices against the coordinate space of the patch
                if (LeftCellIndexX < PatchOriginCellIndexX)
                {
                    LeftCellIndexX = PatchOriginCellIndexX;
                }
                if (RightCellIndexX > PatchCellLimitIndexX)
                {
                    RightCellIndexX = PatchCellLimitIndexX;
                }

                if (LeftCellIndexX <= RightCellIndexX)
                {
                    double Y = ((PatchOriginCellIndexY + ProcessingCellYIndex) * CellSize) + HalfCellSize;

                    for (int I = LeftCellIndexX; I < RightCellIndexX; I++)
                    {
                        double Z = GetHeight(Tri, I * CellSize + HalfCellSize, Y);

                        if (Z != Common.Consts.NullReal)
                        {
                            if (Patch[I - PatchOriginCellIndexX, ProcessingCellYIndex] == Common.Consts.NullHeight)
                            {
                                ValueCount++;
                                Patch[I - PatchOriginCellIndexX, ProcessingCellYIndex] = (float)(Z + OffSet);
                            }
                        }
                    }
                }

                // Recalculate the left and right cell indexers for the next row of cells to be scanned across the triangle.
                H1X += H1SlopeTimesCellSize;
                H2X -= H2SlopeTimesCellSize;

                NumCellRowsToProcess--;
                ProcessingCellYIndex += YStep;

                // if (NumCellRowsToProcess > 0) and not InRange(ProcessingCellYIndex, 0, kSubGridTreeDimension - 1) then
                //   SIGLogMessage.PublishNoODS(Self, Format('ProcessingCellYIndex (%d) out of range', [ProcessingCellYIndex]), ...);
            } while ((NumCellRowsToProcess > 0) && !SingleRowOnly); // or not InRange(ProcessingCellYIndex, 0, kSubGridTreeDimension - 1);
        }
예제 #2
0
 private double GetHeight2(ref Triangle tri, double X, double Y)
 {
     return(XYZ.GetTriangleHeightEx(ref vertexItems[tri.Vertex0], ref vertexItems[tri.Vertex1], ref vertexItems[tri.Vertex2], X, Y));
 }
예제 #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);
        }
예제 #4
0
 private double GetHeight(Triangle tri, double X, double Y)
 {
     return(XYZ.GetTriangleHeight(vertexItems[tri.Vertex0], vertexItems[tri.Vertex1], vertexItems[tri.Vertex2], X, Y));
 }