public bool Equals(BSPoint right) { if (right == null) { return(false); } return(Position == right.Position); }
/** * return true if 3 points are collinear * by that if those 3 points create straight line */ private bool IsCollinear(BSPoint A, BSPoint B, BSPoint C) { // this eventually has to happen on the plane that contains this 3 pages // for now we ignore Z Vector3 Diff1 = B.Position - A.Position; Vector3 Diff2 = C.Position - A.Position; float Result = Diff1.X * Diff2.Y - Diff1.Y * Diff2.X; return(Result == 0.0f); }
public BSTriangle(BSPoint A, BSPoint B) { Vertices[0] = A; Vertices[1] = B; Vertices[2] = B; Center = (A.Position + B.Position) / 2.0f; Vertices[0].AddTriangle(this); Vertices[1].AddTriangle(this); Vertices[2].AddTriangle(this); // now create edges, this should be in the CCW order Edges[0] = new BSHalfEdge(Vertices[0], Vertices[1]); Edges[1] = new BSHalfEdge(Vertices[1], Vertices[2]); Edges[2] = new BSHalfEdge(Vertices[2], Vertices[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); }
//public static bool operator ==(BSTriangle left, BSTriangle right) //{ // return (left.Vertices[0] == right.Vertices[0] && left.Vertices[1] == right.Vertices[1] && left.Vertices[2] == right.Vertices[2]); //} //public static bool operator !=(BSTriangle left, BSTriangle right) //{ // return !(left.Vertices[0] == right.Vertices[0] && left.Vertices[1] == right.Vertices[1] && left.Vertices[2] == right.Vertices[2]); //} //public BSTriangle(BSTriangle Copy) //{ // //FMemory::Memcpy(Vertices, Copy.Vertices); // //FMemory::Memcpy(Edges, Copy.Edges); // //Center = Copy.Center; // //Vertices[0]->AddTriangle(this); // //Vertices[1]->AddTriangle(this); // //Vertices[2]->AddTriangle(this); //} public BSTriangle(BSPoint A, BSPoint B, BSPoint C) { Vertices[0] = A; Vertices[1] = B; Vertices[2] = C; Center = (A.Position + B.Position + C.Position) / 3.0f; Vertices[0].AddTriangle(this); Vertices[1].AddTriangle(this); Vertices[2].AddTriangle(this); // when you make triangle first time, make sure it stays in CCW MakeCCW(); // now create edges, this should be in the CCW order Edges[0] = new BSHalfEdge(Vertices[0], Vertices[1]); Edges[1] = new BSHalfEdge(Vertices[1], Vertices[2]); Edges[2] = new BSHalfEdge(Vertices[2], Vertices[0]); }
/** * return true if all points are coincident * (i.e. if all points are the same) */ private bool AllCoincident(List <BSPoint> InPoints) { if (InPoints.Count > 0) { BSPoint FirstPoint = InPoints[0]; for (int PointIndex = 0; PointIndex < InPoints.Count; ++PointIndex) { BSPoint Point = InPoints[PointIndex]; if (Point.Position != FirstPoint.Position) { return(false); } } return(true); } return(false); }
private void MakeCCW() { // this eventually has to happen on the plane that contains this 3 points // for now we ignore Z Vector3 Diff1 = Vertices[1].Position - Vertices[0].Position; Vector3 Diff2 = Vertices[2].Position - Vertices[0].Position; float Result = Diff1.X * Diff2.Y - Diff1.Y * Diff2.X; //check(Result != 0.0f); // it's in left side, we need this to be right side if (Result < 0.0f) { // swap 1&2 BSPoint TempPt = Vertices[2]; Vertices[2] = Vertices[1]; Vertices[1] = TempPt; } }
public float GetDistance(BSPoint Other) { return((Other.Position - Position).Length()); }
/** * 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); }
/** * return true if they can make triangle */ private bool IsEligibleForTriangulation(BSPoint A, BSPoint B, BSPoint C) { return(IsCollinear(A, B, C) == false); }
/** * 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); } } }
bool Contains(BSPoint Other) { return(Other == Vertices[0] || Other == Vertices[1] || Other == Vertices[2]); }
public BSIndexPoint(BSPoint P, int InOriginalIndex) { Point = P; OriginalIndex = InOriginalIndex; }
public BSHalfEdge(BSPoint A, BSPoint B) { Vertices[0] = A; Vertices[1] = B; }
/** * 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]); }); } } } } } } }