Пример #1
0
        public bool DoesShareSameEdge(BSTriangle Other)
        {
            for (int I = 0; I < 3; ++I)
            {
                for (int J = 0; J < 3; ++J)
                {
                    if (Other.Edges[I].DoesShare(Edges[J]))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Пример #2
0
        public bool HasSameHalfEdge(BSTriangle Other)
        {
            for (int I = 0; I < 3; ++I)
            {
                for (int J = 0; J < 3; ++J)
                {
                    if (Other.Edges[I].Equals(Edges[J]))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Пример #3
0
        /**
         * Flip TriangleList(I) with TriangleList(J).
         */
        private bool FlipTriangles(int TriangleIndexOne, int TriangleIndexTwo)
        {
            BSTriangle A = TriangleList[TriangleIndexOne];
            BSTriangle B = TriangleList[TriangleIndexTwo];

            // if already optimized, don't have to do any
            BSPoint TestPt = A.FindNonSharingPoint(B);

            // If it's not inside, we don't have to do any
            if (GetCircumcircleState(A, TestPt) != ECircumCircleState.Inside)
            {
                return(false);
            }

            BSTriangle[] NewTriangles  = new BSTriangle[2];
            int          TrianglesMade = 0;

            for (int VertexIndexOne = 0; VertexIndexOne < 2; ++VertexIndexOne)
            {
                for (int VertexIndexTwo = VertexIndexOne + 1; VertexIndexTwo < 3; ++VertexIndexTwo)
                {
                    // Check if these vertices form a valid triangle (should be non-colinear)
                    if (IsEligibleForTriangulation(A.Vertices[VertexIndexOne], A.Vertices[VertexIndexTwo], TestPt))
                    {
                        // Create the new triangle and check if the final (original) vertex falls inside or outside of it's circumcircle
                        BSTriangle NewTriangle      = new BSTriangle(A.Vertices[VertexIndexOne], A.Vertices[VertexIndexTwo], TestPt);
                        int        VertexIndexThree = 3 - (VertexIndexTwo + VertexIndexOne);
                        if (GetCircumcircleState(NewTriangle, A.Vertices[VertexIndexThree]) == ECircumCircleState.Outside)
                        {
                            // If so store the triangle and increment the number of triangles
                            //checkf(TrianglesMade < 2, TEXT("Incorrect number of triangles created"));
                            NewTriangles[TrianglesMade] = NewTriangle;
                            ++TrianglesMade;
                        }
                    }
                }
            }

            // In case two triangles were generated the flip was successful so we can add them to the list
            if (TrianglesMade == 2)
            {
                AddTriangle(NewTriangles[0], false);
                AddTriangle(NewTriangles[1], false);
            }

            return(TrianglesMade == 2);
        }
Пример #4
0
        /**
         * Find Triangle this TestPoint is within
         *
         * @param	TestPoint				Point to test
         * @param	OutBaryCentricCoords	Output BaryCentricCoords2D of the point in the triangle // for now it's only 2D
         * @param	OutTriangle				The triangle that this point is within or lie
         * @param	TriangleList			TriangleList to test
         *
         * @return	true if successfully found the triangle this point is within
         */
        public bool FindTriangleThisPointBelongsTo(Vector3 TestPoint, ref Vector3 OutBaryCentricCoords, ref BSTriangle OutTriangle, List <BSTriangle> TriangleList)
        {
            // Calculate distance from point to triangle and sort the triangle list accordingly
            List <BSSortByDistance> SortedTriangles = new List <BSSortByDistance>();

            for (int i = 0; i < TriangleList.Count; ++i)
            {
                SortedTriangles.Add(new BSSortByDistance(-1, 0));
            }
            for (int TriangleIndex = 0; TriangleIndex < TriangleList.Count; ++TriangleIndex)
            {
                SortedTriangles[TriangleIndex].Index    = TriangleIndex;
                SortedTriangles[TriangleIndex].Distance = TriangleList[TriangleIndex].GetDistance(TestPoint);
            }
            SortedTriangles.Sort((A, B) =>
            {
                if (A.Distance == B.Distance)
                {
                    return(0);
                }
                return(A.Distance < B.Distance ? 1 : -1);
            });

            // Now loop over the sorted triangles and test the barycentric coordinates with the point
            for (int i = 0; i < SortedTriangles.Count; ++i)
            {
                BSTriangle Triangle = TriangleList[SortedTriangles[i].Index];

                Vector3 Coords = GetBaryCentric2D(TestPoint, Triangle.Vertices[0].Position, Triangle.Vertices[1].Position, Triangle.Vertices[2].Position);

                // Z coords often has precision error because it's derived from 1-A-B, so do more precise check
                if (Math.Abs(Coords.Z) < 0.00001f)
                {
                    Coords.Z = 0.0f;
                }

                // Is the point inside of the triangle, or on it's edge (Z coordinate should always match since the blend samples are set in 2D)
                if (0.0f <= Coords.X && Coords.X <= 1.0 && 0.0f <= Coords.Y && Coords.Y <= 1.0 && 0.0f <= Coords.Z && Coords.Z <= 1.0)
                {
                    OutBaryCentricCoords = Coords;
                    OutTriangle          = Triangle;
                    return(true);
                }
            }

            return(false);
        }
Пример #5
0
 public void Triangulate()
 {
     if (SamplesList.Count == 0)
     {
         return;
     }
     else if (SamplesList.Count == 1)
     {
         // degenerate case 1
         BSTriangle triangle = new BSTriangle(SamplesList[0]);
         AddTriangle(triangle);
     }
     else if (SamplesList.Count == 2)
     {
         // degenerate case 2
         BSTriangle triangle = new BSTriangle(SamplesList[0], SamplesList[1]);
         AddTriangle(triangle);
     }
     else
     {
         Sort();
         // first choose first 3 points
         for (int i = 2; i < SamplesList.Count; ++i)
         {
             GenerateTriangles(SamplesList, i + 1);
         }
         if (TriangleList.Count == 0)
         {
             if (AllCoincident(SamplesList))
             {
                 // coincident case - just create one triangle
                 BSTriangle triangle = new BSTriangle(SamplesList[0]);
                 AddTriangle(triangle);
             }
             else
             {
                 // collinear case: create degenerate triangles between pairs of points
                 for (int pointIndex = 0; pointIndex < SamplesList.Count - 1; ++pointIndex)
                 {
                     BSTriangle triangle = new BSTriangle(SamplesList[pointIndex], SamplesList[pointIndex + 1]);
                     AddTriangle(triangle);
                 }
             }
         }
     }
 }
Пример #6
0
        /**
         * Add new Triangle
         */
        private void AddTriangle(BSTriangle triangle, bool bCheckHalfEdge = true)
        {
            // see if it's same vertices
            for (int i = 0; i < TriangleList.Count; ++i)
            {
                if (triangle == TriangleList[i])
                {
                    return;
                }

                if (bCheckHalfEdge && triangle.HasSameHalfEdge(TriangleList[i]))
                {
                    return;
                }
            }

            TriangleList.Add(new BSTriangle(triangle.Vertices[0], triangle.Vertices[1], triangle.Vertices[2]));
        }
Пример #7
0
        // find point that doesn't share with this
        // this should only get called if it shares same edge
        public BSPoint FindNonSharingPoint(BSTriangle Other)
        {
            if (!Contains(Other.Vertices[0]))
            {
                return(Other.Vertices[0]);
            }

            if (!Contains(Other.Vertices[1]))
            {
                return(Other.Vertices[1]);
            }

            if (!Contains(Other.Vertices[2]))
            {
                return(Other.Vertices[2]);
            }

            return(null);
        }
Пример #8
0
 public void RemoveTriangle(BSTriangle TriangleToRemove)
 {
     //Triangles.Remove(TriangleToRemove);
 }
Пример #9
0
        //public static bool operator ==(BSPoint left, BSPoint right)
        //{
        //    // if same position, it's same point
        //    return (left.Position == right?.Position);
        //}
        //public static bool operator !=(BSPoint left, BSPoint right)
        //{
        //    // if same position, it's same point
        //    return (left.Position != right.Position);
        //}

        public void AddTriangle(BSTriangle NewTriangle)
        {
            // Triangles.AddUnique(NewTriangle);
        }
Пример #10
0
        /**
         * Used as incremental step to triangulate all points
         * Create triangles TotalNum of PointList
         */
        private int GenerateTriangles(List <BSPoint> PointList, int TotalNum)
        {
            if (TotalNum == 3)
            {
                if (IsEligibleForTriangulation(PointList[0], PointList[1], PointList[2]))
                {
                    BSTriangle Triangle = new BSTriangle(PointList[0], PointList[1], PointList[2]);
                    AddTriangle(Triangle);
                }
            }
            else if (TriangleList.Count == 0)
            {
                BSPoint TestPoint = PointList[TotalNum - 1];

                // so far no triangle is made, try to make it with new points that are just entered
                for (int I = 0; I < TotalNum - 2; ++I)
                {
                    if (IsEligibleForTriangulation(PointList[I], PointList[I + 1], TestPoint))
                    {
                        BSTriangle NewTriangle = new BSTriangle(PointList[I], PointList[I + 1], TestPoint);
                        AddTriangle(NewTriangle);
                    }
                }
            }
            else
            {
                // get the last addition
                BSPoint TestPoint   = PointList[TotalNum - 1];
                int     TriangleNum = TriangleList.Count;

                for (int I = 0; I < TriangleList.Count; ++I)
                {
                    BSTriangle Triangle = TriangleList[I];
                    if (IsEligibleForTriangulation(Triangle.Vertices[0], Triangle.Vertices[1], TestPoint))
                    {
                        BSTriangle NewTriangle = new BSTriangle(Triangle.Vertices[0], Triangle.Vertices[1], TestPoint);
                        AddTriangle(NewTriangle);
                    }

                    if (IsEligibleForTriangulation(Triangle.Vertices[0], Triangle.Vertices[2], TestPoint))
                    {
                        BSTriangle NewTriangle = new BSTriangle(Triangle.Vertices[0], Triangle.Vertices[2], TestPoint);
                        AddTriangle(NewTriangle);
                    }

                    if (IsEligibleForTriangulation(Triangle.Vertices[1], Triangle.Vertices[2], TestPoint))
                    {
                        BSTriangle NewTriangle = new BSTriangle(Triangle.Vertices[1], Triangle.Vertices[2], TestPoint);
                        AddTriangle(NewTriangle);
                    }
                }

                // this is locally optimization part
                // we need to make sure all triangles are locally optimized. If not optimize it.
                for (int I = 0; I < TriangleList.Count; ++I)
                {
                    BSTriangle A = TriangleList[I];
                    for (int J = I + 1; J < TriangleList.Count; ++J)
                    {
                        BSTriangle B = TriangleList[J];

                        // does share same edge
                        if (A.DoesShareSameEdge(B))
                        {
                            // then test to see if locally optimized
                            if (FlipTriangles(I, J))
                            {
                                //// if this flips, remove current triangle
                                //delete TriangleList[I];
                                //delete TriangleList[J];
                                //I need to remove J first because other wise,
                                //  index J isn't valid anymore
                                TriangleList.RemoveAt(J);
                                TriangleList.RemoveAt(I);
                                // start over since we modified triangle
                                // once we don't have any more to flip, we're good to go!
                                I = -1;
                                break;
                            }
                        }
                    }
                }
            }

            return(TriangleList.Count);
        }
Пример #11
0
        /**
         * The key function in Delaunay Triangulation
         * return true if the TestPoint is WITHIN the triangle circumcircle
         *	http://en.wikipedia.org/wiki/Delaunay_triangulation
         */
        private ECircumCircleState GetCircumcircleState(BSTriangle triangle, BSPoint testPoint)
        {
            int NumPointsPerTriangle = 3;

            // First off, normalize all the points
            Vector3[] NormalizedPositions = new Vector3[NumPointsPerTriangle];

            // Unrolled loop
            NormalizedPositions[0].X = (triangle.Vertices[0].Position - GridMin).X * RecipGridSize.X;
            NormalizedPositions[0].Y = (triangle.Vertices[0].Position - GridMin).Y * RecipGridSize.Y;
            NormalizedPositions[0].Z = (triangle.Vertices[0].Position - GridMin).Z * RecipGridSize.Z;
            NormalizedPositions[1].X = (triangle.Vertices[1].Position - GridMin).X * RecipGridSize.X;
            NormalizedPositions[1].Y = (triangle.Vertices[1].Position - GridMin).Y * RecipGridSize.Y;
            NormalizedPositions[1].Z = (triangle.Vertices[1].Position - GridMin).Z * RecipGridSize.Z;
            NormalizedPositions[2].X = (triangle.Vertices[2].Position - GridMin).X * RecipGridSize.X;
            NormalizedPositions[2].Y = (triangle.Vertices[2].Position - GridMin).Y * RecipGridSize.Y;
            NormalizedPositions[2].Z = (triangle.Vertices[2].Position - GridMin).Z * RecipGridSize.Z;

            Vector3 NormalizedTestPoint = Vector3.Zero;

            NormalizedTestPoint.X = (testPoint.Position - GridMin).X * RecipGridSize.X;
            NormalizedTestPoint.Y = (testPoint.Position - GridMin).Y * RecipGridSize.Y;
            NormalizedTestPoint.Z = (testPoint.Position - GridMin).Z * RecipGridSize.Z;

            // ignore Z, eventually this has to be on plane
            // http://en.wikipedia.org/wiki/Delaunay_triangulation - determinant
            float M00 = NormalizedPositions[0].X - NormalizedTestPoint.X;
            float M01 = NormalizedPositions[0].Y - NormalizedTestPoint.Y;
            float M02 = NormalizedPositions[0].X * NormalizedPositions[0].X - NormalizedTestPoint.X * NormalizedTestPoint.X
                        + NormalizedPositions[0].Y * NormalizedPositions[0].Y - NormalizedTestPoint.Y * NormalizedTestPoint.Y;

            float M10 = NormalizedPositions[1].X - NormalizedTestPoint.X;
            float M11 = NormalizedPositions[1].Y - NormalizedTestPoint.Y;
            float M12 = NormalizedPositions[1].X * NormalizedPositions[1].X - NormalizedTestPoint.X * NormalizedTestPoint.X
                        + NormalizedPositions[1].Y * NormalizedPositions[1].Y - NormalizedTestPoint.Y * NormalizedTestPoint.Y;

            float M20 = NormalizedPositions[2].X - NormalizedTestPoint.X;
            float M21 = NormalizedPositions[2].Y - NormalizedTestPoint.Y;
            float M22 = NormalizedPositions[2].X * NormalizedPositions[2].X - NormalizedTestPoint.X * NormalizedTestPoint.X
                        + NormalizedPositions[2].Y * NormalizedPositions[2].Y - NormalizedTestPoint.Y * NormalizedTestPoint.Y;

            float Det = M00 * M11 * M22 + M01 * M12 * M20 + M02 * M10 * M21 - (M02 * M11 * M20 + M01 * M10 * M22 + M00 * M12 * M21);

            // When the vertices are sorted in a counterclockwise order, the determinant is positive if and only if Testpoint lies inside the circumcircle of T.
            if (Det < 0.0f)
            {
                return(ECircumCircleState.Outside);
            }
            else
            {
                // On top of the triangle edge
                if (Math.Abs(Det) < 0.00001f)
                {
                    return(ECircumCircleState.On);
                }
                else
                {
                    return(ECircumCircleState.Inside);
                }
            }
        }
Пример #12
0
        /**
         * Fill up Grid GridPoints using TriangleList input - Grid information should have been set by SetGridInfo
         *
         * @param	SamplePoints		: Sample Point List
         * @param	TriangleList		: List of triangles
         */
        public void GenerateGridElements(List <BSPoint> SamplePoints, List <BSTriangle> TriangleList)
        {
            if (!(NumGridDivisions.X > 0 && NumGridDivisions.Y > 0))
            {
                return;
            }
            //if(!(GridDimensions.IsValid))

            int TotalNumGridPoints = (int)(NumGridPointsForAxis.X * NumGridPointsForAxis.Y);

            GridPoints.Clear();


            if (SamplePoints.Count == 0 || TriangleList.Count == 0)
            {
                return;
            }

            for (int i = 0; i < TotalNumGridPoints; ++i)
            {
                GridPoints.Add(new GridElement());
            }
            Vector3 GridPointPosition;

            for (int GridPositionX = 0; GridPositionX < NumGridPointsForAxis.X; ++GridPositionX)
            {
                for (int GridPositionY = 0; GridPositionY < NumGridPointsForAxis.Y; ++GridPositionY)
                {
                    BSTriangle  SelectedTriangle = null;
                    GridElement GridPoint        = GridPoints[GridPositionX * (int)NumGridPointsForAxis.Y + GridPositionY];

                    GridPointPosition = GetPosFromIndex(GridPositionX, GridPositionY);

                    Vector3 Weights = Vector3.Zero;
                    if (FindTriangleThisPointBelongsTo(GridPointPosition, ref Weights, ref SelectedTriangle, TriangleList))
                    {
                        // found it
                        GridPoint.Weights[0] = Weights.X;
                        GridPoint.Weights[1] = Weights.Y;
                        GridPoint.Weights[2] = Weights.Z;
                        // need to find sample point index
                        // @todo fix this with better solution
                        // lazy me
                        GridPoint.Indices[0] = SamplePoints.FindIndex((point) => { return(point == SelectedTriangle.Vertices[0]); });
                        GridPoint.Indices[1] = SamplePoints.FindIndex((point) => { return(point == SelectedTriangle.Vertices[1]); });
                        GridPoint.Indices[2] = SamplePoints.FindIndex((point) => { return(point == SelectedTriangle.Vertices[2]); });

                        //check(GridPoint.Indices[0] != INDEX_NONE);
                        //check(GridPoint.Indices[1] != INDEX_NONE);
                        //check(GridPoint.Indices[2] != INDEX_NONE);
                    }
                    else
                    {
                        List <BSSortByDistance> SortedTriangles = new List <BSSortByDistance>();
                        for (int TriangleIndex = 0; TriangleIndex < TriangleList.Count; ++TriangleIndex)
                        {
                            // Check if points are collinear
                            BSTriangle Triangle = TriangleList[TriangleIndex];
                            Vector3    EdgeA    = Triangle.Vertices[1].Position - Triangle.Vertices[0].Position;
                            Vector3    EdgeB    = Triangle.Vertices[2].Position - Triangle.Vertices[0].Position;
                            float      Result   = EdgeA.X * EdgeB.Y - EdgeA.Y * EdgeB.X;
                            // Only add valid triangles
                            if (Result > 0.0f)
                            {
                                SortedTriangles.Add(new BSSortByDistance(TriangleIndex, Triangle.GetDistance(GridPointPosition)));
                            }
                        }

                        if (SortedTriangles.Count > 0)
                        {
                            // SortedTriangles.Sort([](FSortByDistance A, FSortByDistance B) { return A.Distance < B.Distance; });
                            BSTriangle ClosestTriangle = TriangleList[SortedTriangles[0].Index];

                            // For the closest triangle, determine which of its edges is closest to the grid point
                            List <BSSortByDistance> Edges         = new List <BSSortByDistance>();
                            List <Vector3>          PointsOnEdges = new List <Vector3>();
                            for (int EdgeIndex = 0; EdgeIndex < 3; ++EdgeIndex)
                            {
                                Vector3 ClosestPoint = ClosestPointOnLine(ClosestTriangle.Edges[EdgeIndex].Vertices[0].Position, ClosestTriangle.Edges[EdgeIndex].Vertices[1].Position, GridPointPosition);
                                Edges.Add(new BSSortByDistance(EdgeIndex, (ClosestPoint - GridPointPosition).LengthSquared()));
                                PointsOnEdges.Add(ClosestPoint);
                            }
                            //Edges.Sort([](FSortByDistance A, FSortByDistance B) { return A.Distance < B.Distance; });

                            // Calculate weighting using the closest edge points and the clamped grid position on the line
                            Vector3 GridWeights = GetBaryCentric2D(PointsOnEdges[Edges[0].Index], ClosestTriangle.Vertices[0].Position, ClosestTriangle.Vertices[1].Position, ClosestTriangle.Vertices[2].Position);

                            for (int Index = 0; Index < 3; ++Index)
                            {
                                GridPoint.Weights[Index] = GridWeights[Index];
                                GridPoint.Indices[Index] = SamplePoints.FindIndex((point) => { return(point == ClosestTriangle.Vertices[Index]); });
                            }
                        }
                        else
                        {
                            // This means that there is either one point, two points or collinear triangles on the grid
                            if (SamplePoints.Count == 1)
                            {
                                // Just one, fill all grid points to the single sample
                                GridPoint.Weights[0] = 1.0f;
                                GridPoint.Indices[0] = 0;
                            }
                            else
                            {
                                // Two points or co-linear triangles, first find the two closest samples
                                List <BSSortByDistance> SampleDistances = new List <BSSortByDistance>();
                                for (int PointIndex = 0; PointIndex < SamplePoints.Count; ++PointIndex)
                                {
                                    var     vec     = (SamplePoints[PointIndex].Position - GridPointPosition);
                                    Vector2 vector2 = new Vector2(vec.X, vec.Y);
                                    float   DistanceFromSampleToPoint = vector2.LengthSquared();
                                    SampleDistances.Add(new BSSortByDistance(PointIndex, DistanceFromSampleToPoint));
                                }
                                SampleDistances.Sort((A, B) =>
                                {
                                    if (A.Distance == B.Distance)
                                    {
                                        return(0);
                                    }
                                    return(A.Distance > B.Distance ? 1 : -1);
                                });

                                // Find closest point on line between the two samples (clamping the grid position to the line, just like clamping to the triangle edges)
                                BSPoint[] Samples = new BSPoint[2];
                                Samples[0] = SamplePoints[SampleDistances[0].Index];
                                Samples[1] = SamplePoints[SampleDistances[1].Index];
                                Vector3 ClosestPointOnTheLine = ClosestPointOnLine(Samples[0].Position, Samples[1].Position, GridPointPosition);

                                var     temp        = (Samples[0].Position - Samples[1].Position);
                                Vector2 tempVector2 = new Vector2(temp.X, temp.Y);
                                float   LineLength  = tempVector2.LengthSquared();

                                // Weight the samples according to the distance from the grid point on the line to the samples
                                for (int SampleIndex = 0; SampleIndex < 2; ++SampleIndex)
                                {
                                    var thelength3D = (Samples[SampleIndex].Position - ClosestPointOnTheLine);
                                    var thelength2D = new Vector2(thelength3D.X, thelength3D.Y);
                                    GridPoint.Weights[SampleIndex] = (LineLength - thelength2D.LengthSquared()) / LineLength;
                                    GridPoint.Indices[SampleIndex] = SamplePoints.FindIndex((point) => { return(point == Samples[SampleIndex]); });
                                }
                            }
                        }
                    }
                }
            }
        }