Exemplo n.º 1
0
        public void ScanCellsOverTriangle(ISubGridTree tree,
                                          int triIndex,
                                          Func <ISubGridTree, int, int, bool> leafSatisfied,
                                          Action <ISubGridTree, int, int, int> includeTriangleInLeaf,
                                          Action <ISubGridTree,
                                                  int,                                  // sourceTriangle
                                                  Func <ISubGridTree, int, int, bool>,  // leafSatisfied
                                                  Action <ISubGridTree, int, int, int>, // includeTriangleInLeaf
                                                  XYZ, XYZ, XYZ, bool> ProcessTrianglePiece)
        {
            Triangle Tri = TriangleItems[triIndex];

            // Split triangle into two pieces, a 'top' piece and a 'bottom' piece to simplify
            // scanning across the triangle. Split is always with a horizontal line

            XYZ[] SortVertices = new XYZ[]
            {
                VertexItems[Tri.Vertex0],
                VertexItems[Tri.Vertex1],
                VertexItems[Tri.Vertex2]
            };

            if (SortVertices[0].Y > SortVertices[1].Y)
            {
                DesignGeometry.SwapVertices(ref SortVertices[0], ref SortVertices[1]);
            }
            if (SortVertices[1].Y > SortVertices[2].Y)
            {
                DesignGeometry.SwapVertices(ref SortVertices[1], ref SortVertices[2]);
            }
            if (SortVertices[0].Y > SortVertices[1].Y)
            {
                DesignGeometry.SwapVertices(ref SortVertices[0], ref SortVertices[1]);
            }

            XYZ TopVertex     = SortVertices[2];
            XYZ CentralVertex = SortVertices[1];
            XYZ BottomVertex  = SortVertices[0];

            // now make sure leftmost vertex in in first array item
            if (SortVertices[0].X > SortVertices[1].X)
            {
                DesignGeometry.SwapVertices(ref SortVertices[0], ref SortVertices[1]);
            }
            if (SortVertices[1].X > SortVertices[2].X)
            {
                DesignGeometry.SwapVertices(ref SortVertices[1], ref SortVertices[2]);
            }
            if (SortVertices[0].X > SortVertices[1].X)
            {
                DesignGeometry.SwapVertices(ref SortVertices[0], ref SortVertices[1]);
            }

            XYZ LeftMostVertex  = SortVertices[0];
            XYZ RightMostVertex = SortVertices[2];

            // Are top or bottom vertices coincident with the middle vertex
            bool BottomPieceOnly = Math.Abs(TopVertex.Y - CentralVertex.Y) < 0.0001;
            bool TopPieceOnly    = Math.Abs(BottomVertex.Y - CentralVertex.Y) < 0.0001;

            if (TopPieceOnly && BottomPieceOnly) // It's a thin horizontal triangle
            {
                ProcessTrianglePiece(tree, triIndex, leafSatisfied, includeTriangleInLeaf, LeftMostVertex, RightMostVertex, CentralVertex, true);
            }
            else
            {
                if (!(TopPieceOnly || BottomPieceOnly))
                {
                    // Divide triangle in two with a horizontal line
                    // Find intersection point of triangle edge between top most and bottom most vertices
                    if (LineIntersection.LinesIntersect(LeftMostVertex.X - 1, CentralVertex.Y,
                                                        RightMostVertex.X + 1, CentralVertex.Y,
                                                        TopVertex.X, TopVertex.Y,
                                                        BottomVertex.X, BottomVertex.Y,
                                                        out double IntersectX, out double IntersectY, true, out _))
                    {
                        XYZ IntersectionVertex = new XYZ(IntersectX, IntersectY, 0);
                        ProcessTrianglePiece(tree, triIndex, leafSatisfied, includeTriangleInLeaf, CentralVertex, IntersectionVertex, TopVertex, false);
                        ProcessTrianglePiece(tree, triIndex, leafSatisfied, includeTriangleInLeaf, CentralVertex, IntersectionVertex, BottomVertex, false);
                    }
                    else
                    {
                        Log.LogWarning($"Triangle {Tri} failed to have intersection line calculated for it");
                    }
                }
Exemplo n.º 2
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);
        }