public static List <Contour> SubdivideContours(List <Contour> a_rOuterContours, float a_fStep) { List <Contour> oIteratedContoursList = new List <Contour>(a_rOuterContours.Count); foreach (Contour rOuterContour in a_rOuterContours) { // Simplify the new outer contour List <Vector2> oOuterContourVertices = new List <Vector2>(rOuterContour.Vertices); List <Vector2> oIteratedContourVertices = new List <Vector2>(); for (int i = 0; i < oOuterContourVertices.Count; ++i) { Vector2 f2SegmentBegin = oOuterContourVertices[i]; Vector2 f2SegmentEnd = oOuterContourVertices[(i + 1) % oOuterContourVertices.Count]; Vector2 f2SegmentDirection = f2SegmentEnd - f2SegmentBegin; float fSegmentLength = f2SegmentDirection.magnitude; int iSubdivisionCount = Mathf.RoundToInt(fSegmentLength / a_fStep); //iSubdivisionCount = 2; oIteratedContourVertices.Add(f2SegmentBegin); if (iSubdivisionCount > 1) { f2SegmentDirection /= fSegmentLength; float fSubdivisionStep = fSegmentLength / (float)(iSubdivisionCount); for (int iSubdivision = 1; iSubdivision < iSubdivisionCount; ++iSubdivision) { oIteratedContourVertices.Add(f2SegmentBegin + f2SegmentDirection * fSubdivisionStep * iSubdivision); } } } if (oIteratedContourVertices.Count > 2) { // Create the contour Contour oIteratedContour = new Contour(rOuterContour.Region); oIteratedContour.AddLast(oIteratedContourVertices); // Add the contour to the list oIteratedContoursList.Add(oIteratedContour); } } return(oIteratedContoursList); }
public static List<Contour> SimplifyContours( List<Contour> a_rOuterContours, float a_fAccuracy ) { List<Contour> oSimplifiedContoursList = new List<Contour>( a_rOuterContours.Count ); foreach( Contour rOuterContour in a_rOuterContours ) { // Simplify the new outer contour List<Vector2> oOuterContourVertices = new List<Vector2>( rOuterContour.Vertices ); List<Vector2> oSimplifiedOuterContourVertices = RamerDouglasPeuker( oOuterContourVertices, 0, oOuterContourVertices.Count - 1, a_fAccuracy ); if( oSimplifiedOuterContourVertices.Count > 2 ) { // Create the contour Contour oSimplifiedOuterContour = new Contour( rOuterContour.Region ); oSimplifiedOuterContour.AddLast( oSimplifiedOuterContourVertices ); // Add the contour to the list oSimplifiedContoursList.Add( oSimplifiedOuterContour ); } } return oSimplifiedContoursList; }
public static List <Contour> SimplifyContours(List <Contour> a_rOuterContours, float a_fAccuracy) { List <Contour> oSimplifiedContoursList = new List <Contour>(a_rOuterContours.Count); foreach (Contour rOuterContour in a_rOuterContours) { // Simplify the new outer contour List <Vector2> oOuterContourVertices = new List <Vector2>(rOuterContour.Vertices); List <Vector2> oSimplifiedOuterContourVertices = RamerDouglasPeuker(oOuterContourVertices, 0, oOuterContourVertices.Count - 1, a_fAccuracy); if (oSimplifiedOuterContourVertices.Count > 2) { // Create the contour Contour oSimplifiedOuterContour = new Contour(rOuterContour.Region); oSimplifiedOuterContour.AddLast(oSimplifiedOuterContourVertices); // Add the contour to the list oSimplifiedContoursList.Add(oSimplifiedOuterContour); } } return(oSimplifiedContoursList); }
// Finds a pair of inner contour vertex / outer contour vertex that are mutually visible public static Contour InsertInnerContourIntoOuterContour(Contour a_rOuterContour, Contour a_rInnerContour) { // Look for the inner vertex of maximum x-value Vector2 f2InnerContourVertexMax = Vector2.one * int.MinValue; CircularLinkedListNode <Vector2> rMutualVisibleInnerContourVertexNode = null; CircularLinkedList <Vector2> rInnerContourVertexList = a_rInnerContour.Vertices; CircularLinkedListNode <Vector2> rInnerContourVertexNode = rInnerContourVertexList.First; do { // x-value Vector2 f2InnerContourVertex = rInnerContourVertexNode.Value; // New max x found if (f2InnerContourVertexMax.x < f2InnerContourVertex.x) { f2InnerContourVertexMax = f2InnerContourVertex; rMutualVisibleInnerContourVertexNode = rInnerContourVertexNode; } // Go to next vertex rInnerContourVertexNode = rInnerContourVertexNode.Next; }while(rInnerContourVertexNode != rInnerContourVertexList.First); // Visibility ray Ray oInnerVertexVisibilityRay = new Ray(f2InnerContourVertexMax, Vector3.right); float fClosestDistance = int.MaxValue; Vector2 f2ClosestOuterEdgeStart = Vector2.zero; Vector2 f2ClosestOuterEdgeEnd = Vector2.zero; Contour rOuterCutContour = new Contour(a_rOuterContour.Region); rOuterCutContour.AddLast(a_rOuterContour.Vertices); CircularLinkedList <Vector2> rOuterCutContourVertexList = rOuterCutContour.Vertices; CircularLinkedListNode <Vector2> rOuterContourVertexEdgeStart = null; // Raycast from the inner contour vertex to every edge CircularLinkedListNode <Vector2> rOuterContourVertexNode = rOuterCutContourVertexList.First; do { // Construct outer edge from current and next outer contour vertices Vector2 f2OuterEdgeStart = rOuterContourVertexNode.Value; Vector2 f2OuterEdgeEnd = rOuterContourVertexNode.Next.Value; Vector2 f2OuterEdge = f2OuterEdgeEnd - f2OuterEdgeStart; // Orthogonal vector to edge (pointing to polygon interior) Vector2 f2OuterEdgeNormal = Uni2DMathUtils.PerpVector2(f2OuterEdge); // Vector from edge start to inner vertex Vector2 f2OuterEdgeStartToInnerVertex = f2InnerContourVertexMax - f2OuterEdgeStart; // If the inner vertex is on the left of the edge (interior), // test if there's any intersection if (Vector2.Dot(f2OuterEdgeStartToInnerVertex, f2OuterEdgeNormal) >= 0.0f) { float fDistanceT; // If visibility intersects outer edge... if (Uni2DMathUtils.Raycast2DSegment(oInnerVertexVisibilityRay, f2OuterEdgeStart, f2OuterEdgeEnd, out fDistanceT) == true) { // Is it the closest intersection we found? if (fClosestDistance > fDistanceT) { fClosestDistance = fDistanceT; rOuterContourVertexEdgeStart = rOuterContourVertexNode; f2ClosestOuterEdgeStart = f2OuterEdgeStart; f2ClosestOuterEdgeEnd = f2OuterEdgeEnd; } } } // Go to next edge rOuterContourVertexNode = rOuterContourVertexNode.Next; }while(rOuterContourVertexNode != rOuterCutContourVertexList.First); // Take the vertex of maximum x-value from the closest intersected edge Vector2 f2ClosestVisibleOuterContourVertex; CircularLinkedListNode <Vector2> rMutualVisibleOuterContourVertexNode; if (f2ClosestOuterEdgeStart.x < f2ClosestOuterEdgeEnd.x) { f2ClosestVisibleOuterContourVertex = f2ClosestOuterEdgeEnd; rMutualVisibleOuterContourVertexNode = rOuterContourVertexEdgeStart.Next; } else { f2ClosestVisibleOuterContourVertex = f2ClosestOuterEdgeStart; rMutualVisibleOuterContourVertexNode = rOuterContourVertexEdgeStart; } // Looking for points inside the triangle defined by inner vertex, intersection point an closest outer vertex // If a point is inside this triangle, at least one is a reflex vertex // The closest reflex vertex which minimises the angle this-vertex/inner vertex/intersection vertex // would be choosen as the mutual visible vertex Vector3 f3IntersectionPoint = oInnerVertexVisibilityRay.GetPoint(fClosestDistance); Vector2 f2InnerContourVertexToIntersectionPoint = new Vector2(f3IntersectionPoint.x, f3IntersectionPoint.y) - f2InnerContourVertexMax; Vector2 f2NormalizedInnerContourVertexToIntersectionPoint = f2InnerContourVertexToIntersectionPoint.normalized; float fMaxDotAngle = float.MinValue; float fMinDistance = float.MaxValue; rOuterContourVertexNode = rOuterCutContourVertexList.First; do { Vector2 f2OuterContourVertex = rOuterContourVertexNode.Value; // if vertex not part of triangle if (f2OuterContourVertex != f2ClosestVisibleOuterContourVertex) { // if vertex is inside triangle... if (Uni2DMathUtils.IsPointInsideTriangle(f2InnerContourVertexMax, f3IntersectionPoint, f2ClosestVisibleOuterContourVertex, f2OuterContourVertex) == true) { // if vertex is reflex Vector2 f2PreviousOuterContourVertex = rOuterContourVertexNode.Previous.Value; Vector2 f2NextOuterContourVertex = rOuterContourVertexNode.Next.Value; if (IsReflexVertex(f2OuterContourVertex, f2PreviousOuterContourVertex, f2NextOuterContourVertex) == true) { // Use dot product as distance Vector2 f2InnerContourVertexToReflexVertex = f2OuterContourVertex - f2InnerContourVertexMax; // INFO: f2NormalizedInnerContourVertexToIntersectionPoint == Vector3.right (if everything is right) float fDistance = Vector2.Dot(f2NormalizedInnerContourVertexToIntersectionPoint, f2InnerContourVertexToReflexVertex); float fDotAngle = Vector2.Dot(f2NormalizedInnerContourVertexToIntersectionPoint, f2InnerContourVertexToReflexVertex.normalized); // New mutual visible vertex if angle smaller (e.g. dot angle larger) than min or equal and closer if (fDotAngle > fMaxDotAngle || (fDotAngle == fMaxDotAngle && fDistance < fMinDistance)) { fMaxDotAngle = fDotAngle; fMinDistance = fDistance; rMutualVisibleOuterContourVertexNode = rOuterContourVertexNode; } } } } // Go to next vertex rOuterContourVertexNode = rOuterContourVertexNode.Next; }while(rOuterContourVertexNode != rOuterCutContourVertexList.First); // Insert now the cut into the polygon // The cut starts from the outer contour mutual visible vertex to the inner vertex CircularLinkedListNode <Vector2> rOuterContourVertexNodeToInsertBefore = rMutualVisibleOuterContourVertexNode.Next; // Loop over the inner contour starting from the inner contour vertex... rInnerContourVertexNode = rMutualVisibleInnerContourVertexNode; do { // ... add the inner contour vertex before the outer contour vertex after the cut rOuterCutContourVertexList.AddBefore(rOuterContourVertexNodeToInsertBefore, rInnerContourVertexNode.Value); rInnerContourVertexNode = rInnerContourVertexNode.Next; }while(rInnerContourVertexNode != rMutualVisibleInnerContourVertexNode); // Close the cut by doubling the inner and outer contour vertices rOuterCutContourVertexList.AddBefore(rOuterContourVertexNodeToInsertBefore, rMutualVisibleInnerContourVertexNode.Value); rOuterCutContourVertexList.AddBefore(rOuterContourVertexNodeToInsertBefore, rMutualVisibleOuterContourVertexNode.Value); return(rOuterCutContour); }
// Finds a pair of inner contour vertex / outer contour vertex that are mutually visible public static Contour InsertInnerContourIntoOuterContour( Contour a_rOuterContour, Contour a_rInnerContour ) { // Look for the inner vertex of maximum x-value Vector2 f2InnerContourVertexMax = Vector2.one * int.MinValue; CircularLinkedListNode<Vector2> rMutualVisibleInnerContourVertexNode = null; CircularLinkedList<Vector2> rInnerContourVertexList = a_rInnerContour.Vertices; CircularLinkedListNode<Vector2> rInnerContourVertexNode = rInnerContourVertexList.First; do { // x-value Vector2 f2InnerContourVertex = rInnerContourVertexNode.Value; // New max x found if( f2InnerContourVertexMax.x < f2InnerContourVertex.x ) { f2InnerContourVertexMax = f2InnerContourVertex; rMutualVisibleInnerContourVertexNode = rInnerContourVertexNode; } // Go to next vertex rInnerContourVertexNode = rInnerContourVertexNode.Next; } while( rInnerContourVertexNode != rInnerContourVertexList.First ); // Visibility ray Ray oInnerVertexVisibilityRay = new Ray( f2InnerContourVertexMax, Vector3.right ); float fClosestDistance = int.MaxValue; Vector2 f2ClosestOuterEdgeStart = Vector2.zero; Vector2 f2ClosestOuterEdgeEnd = Vector2.zero; Contour rOuterCutContour = new Contour( a_rOuterContour.Region ); rOuterCutContour.AddLast( a_rOuterContour.Vertices ); CircularLinkedList<Vector2> rOuterCutContourVertexList = rOuterCutContour.Vertices; CircularLinkedListNode<Vector2> rOuterContourVertexEdgeStart = null; // Raycast from the inner contour vertex to every edge CircularLinkedListNode<Vector2> rOuterContourVertexNode = rOuterCutContourVertexList.First; do { // Construct outer edge from current and next outer contour vertices Vector2 f2OuterEdgeStart = rOuterContourVertexNode.Value; Vector2 f2OuterEdgeEnd = rOuterContourVertexNode.Next.Value; Vector2 f2OuterEdge = f2OuterEdgeEnd - f2OuterEdgeStart; // Orthogonal vector to edge (pointing to polygon interior) Vector2 f2OuterEdgeNormal = Uni2DMathUtils.PerpVector2( f2OuterEdge ); // Vector from edge start to inner vertex Vector2 f2OuterEdgeStartToInnerVertex = f2InnerContourVertexMax - f2OuterEdgeStart; // If the inner vertex is on the left of the edge (interior), // test if there's any intersection if( Vector2.Dot( f2OuterEdgeStartToInnerVertex, f2OuterEdgeNormal ) >= 0.0f ) { float fDistanceT; // If visibility intersects outer edge... if( Uni2DMathUtils.Raycast2DSegment( oInnerVertexVisibilityRay, f2OuterEdgeStart, f2OuterEdgeEnd, out fDistanceT ) == true ) { // Is it the closest intersection we found? if( fClosestDistance > fDistanceT ) { fClosestDistance = fDistanceT; rOuterContourVertexEdgeStart = rOuterContourVertexNode; f2ClosestOuterEdgeStart = f2OuterEdgeStart; f2ClosestOuterEdgeEnd = f2OuterEdgeEnd; } } } // Go to next edge rOuterContourVertexNode = rOuterContourVertexNode.Next; } while( rOuterContourVertexNode != rOuterCutContourVertexList.First ); // Take the vertex of maximum x-value from the closest intersected edge Vector2 f2ClosestVisibleOuterContourVertex; CircularLinkedListNode<Vector2> rMutualVisibleOuterContourVertexNode; if( f2ClosestOuterEdgeStart.x < f2ClosestOuterEdgeEnd.x ) { f2ClosestVisibleOuterContourVertex = f2ClosestOuterEdgeEnd; rMutualVisibleOuterContourVertexNode = rOuterContourVertexEdgeStart.Next; } else { f2ClosestVisibleOuterContourVertex = f2ClosestOuterEdgeStart; rMutualVisibleOuterContourVertexNode = rOuterContourVertexEdgeStart; } // Looking for points inside the triangle defined by inner vertex, intersection point an closest outer vertex // If a point is inside this triangle, at least one is a reflex vertex // The closest reflex vertex which minimises the angle this-vertex/inner vertex/intersection vertex // would be choosen as the mutual visible vertex Vector3 f3IntersectionPoint = oInnerVertexVisibilityRay.GetPoint( fClosestDistance ); Vector2 f2InnerContourVertexToIntersectionPoint = new Vector2( f3IntersectionPoint.x, f3IntersectionPoint.y ) - f2InnerContourVertexMax; Vector2 f2NormalizedInnerContourVertexToIntersectionPoint = f2InnerContourVertexToIntersectionPoint.normalized; float fMaxDotAngle = float.MinValue; float fMinDistance = float.MaxValue; rOuterContourVertexNode = rOuterCutContourVertexList.First; do { Vector2 f2OuterContourVertex = rOuterContourVertexNode.Value; // if vertex not part of triangle if( f2OuterContourVertex != f2ClosestVisibleOuterContourVertex ) { // if vertex is inside triangle... if( Uni2DMathUtils.IsPointInsideTriangle( f2InnerContourVertexMax, f3IntersectionPoint, f2ClosestVisibleOuterContourVertex, f2OuterContourVertex ) == true ) { // if vertex is reflex Vector2 f2PreviousOuterContourVertex = rOuterContourVertexNode.Previous.Value; Vector2 f2NextOuterContourVertex = rOuterContourVertexNode.Next.Value; if( IsReflexVertex( f2OuterContourVertex, f2PreviousOuterContourVertex, f2NextOuterContourVertex ) == true ) { // Use dot product as distance Vector2 f2InnerContourVertexToReflexVertex = f2OuterContourVertex - f2InnerContourVertexMax; // INFO: f2NormalizedInnerContourVertexToIntersectionPoint == Vector3.right (if everything is right) float fDistance = Vector2.Dot( f2NormalizedInnerContourVertexToIntersectionPoint, f2InnerContourVertexToReflexVertex ); float fDotAngle = Vector2.Dot( f2NormalizedInnerContourVertexToIntersectionPoint, f2InnerContourVertexToReflexVertex.normalized ); // New mutual visible vertex if angle smaller (e.g. dot angle larger) than min or equal and closer if( fDotAngle > fMaxDotAngle || ( fDotAngle == fMaxDotAngle && fDistance < fMinDistance ) ) { fMaxDotAngle = fDotAngle; fMinDistance = fDistance; rMutualVisibleOuterContourVertexNode = rOuterContourVertexNode; } } } } // Go to next vertex rOuterContourVertexNode = rOuterContourVertexNode.Next; } while( rOuterContourVertexNode != rOuterCutContourVertexList.First ); // Insert now the cut into the polygon // The cut starts from the outer contour mutual visible vertex to the inner vertex CircularLinkedListNode<Vector2> rOuterContourVertexNodeToInsertBefore = rMutualVisibleOuterContourVertexNode.Next; // Loop over the inner contour starting from the inner contour vertex... rInnerContourVertexNode = rMutualVisibleInnerContourVertexNode; do { // ... add the inner contour vertex before the outer contour vertex after the cut rOuterCutContourVertexList.AddBefore( rOuterContourVertexNodeToInsertBefore, rInnerContourVertexNode.Value ); rInnerContourVertexNode = rInnerContourVertexNode.Next; } while( rInnerContourVertexNode != rMutualVisibleInnerContourVertexNode ); // Close the cut by doubling the inner and outer contour vertices rOuterCutContourVertexList.AddBefore( rOuterContourVertexNodeToInsertBefore, rMutualVisibleInnerContourVertexNode.Value ); rOuterCutContourVertexList.AddBefore( rOuterContourVertexNodeToInsertBefore, rMutualVisibleOuterContourVertexNode.Value ); return rOuterCutContour; }
public static List<Contour> SubdivideContours( List<Contour> a_rOuterContours, float a_fStep ) { List<Contour> oIteratedContoursList = new List<Contour>( a_rOuterContours.Count ); foreach( Contour rOuterContour in a_rOuterContours ) { // Simplify the new outer contour List<Vector2> oOuterContourVertices = new List<Vector2>( rOuterContour.Vertices ); List<Vector2> oIteratedContourVertices = new List<Vector2>(); for(int i = 0; i < oOuterContourVertices.Count; ++i) { Vector2 f2SegmentBegin = oOuterContourVertices[i]; Vector2 f2SegmentEnd = oOuterContourVertices[(i+1)%oOuterContourVertices.Count]; Vector2 f2SegmentDirection = f2SegmentEnd - f2SegmentBegin; float fSegmentLength = f2SegmentDirection.magnitude; int iSubdivisionCount = Mathf.RoundToInt(fSegmentLength / a_fStep); //iSubdivisionCount = 2; oIteratedContourVertices.Add(f2SegmentBegin); if(iSubdivisionCount > 1) { f2SegmentDirection /= fSegmentLength; float fSubdivisionStep = fSegmentLength/(float)(iSubdivisionCount); for(int iSubdivision = 1; iSubdivision < iSubdivisionCount; ++iSubdivision) { oIteratedContourVertices.Add(f2SegmentBegin + f2SegmentDirection * fSubdivisionStep * iSubdivision); } } } if( oIteratedContourVertices.Count > 2 ) { // Create the contour Contour oIteratedContour = new Contour( rOuterContour.Region ); oIteratedContour.AddLast( oIteratedContourVertices ); // Add the contour to the list oIteratedContoursList.Add( oIteratedContour ); } } return oIteratedContoursList; }