Class used as a data container and helper for the texture-to-vertices code.
        private static bool InPolygon(PolygonCreationAssistance pca, ref Vertices polygon, Vector2 point)
        {
            bool inPolygon = !DistanceToHullAcceptable(pca, polygon, point, true);

            if (!inPolygon)
            {
                List <CrossingEdgeInfo> edges = GetCrossingEdges(polygon, EdgeAlignment.Vertical, (int)point.Y);

                if (edges.Count > 0 && edges.Count % 2 == 0)
                {
                    for (int i = 0; i < edges.Count; i += 2)
                    {
                        if (edges[i].CrossingPoint.X <= point.X && edges[i + 1].CrossingPoint.X >= point.X)
                        {
                            return(true);
                        }
                    }

                    return(false);
                }
                return(false);
            }

            return(true);
        }
        /// <summary>
        /// Detects the vertices of the supplied texture data.
        /// </summary>
        /// <param name="data">The texture data.</param>
        /// <param name="width">The texture width.</param>
        /// <param name="height">The texture height.</param>
        /// <returns></returns>
        public static Vertices DetectVertices(uint[] data, int width, int height)
        {
            PolygonCreationAssistance pca = new PolygonCreationAssistance(data, width, height);
            List<Vertices> verts = DetectVertices(ref pca);

            return verts[0];
        }
        public static Vertices CreateVertices(uint[] data, int width, int height)
        {
            PolygonCreationAssistance pca   = new PolygonCreationAssistance(data, width, height);
            List <Vertices>           verts = CreateVertices(pca);

            return(verts[0]);
        }
        private static bool GetNextHullEntrance(PolygonCreationAssistance pca, Vector2 start, out Vector2?entrance)
        {
            // Search for first solid pixel.
            int size = pca.Height * pca.Width;
            int x;

            bool foundTransparent = false;

            for (int i = (int)start.X + (int)start.Y * pca.Width; i <= size; i++)
            {
                if (pca.IsSolid(i))
                {
                    if (foundTransparent)
                    {
                        x = i % pca.Width;

                        entrance = new Vector2(x, (i - x) / pca.Width);
                        return(true);
                    }
                }
                else
                {
                    foundTransparent = true;
                }
            }

            // If there are no solid pixels.
            entrance = null;
            return(false);
        }
        private static bool GetNextHullPoint(PolygonCreationAssistance pca, ref Vector2 last, ref Vector2 current,
                                             out Vector2 next)
        {
            int x;
            int y;

            int indexOfFirstPixelToCheck = GetIndexOfFirstPixelToCheck(last, current);
            int indexOfPixelToCheck;

            const int pixelsToCheck = 8; // _closePixels.Length;

            for (int i = 0; i < pixelsToCheck; i++)
            {
                indexOfPixelToCheck = (indexOfFirstPixelToCheck + i) % pixelsToCheck;

                x = (int)current.X + ClosePixels[indexOfPixelToCheck, 0];
                y = (int)current.Y + ClosePixels[indexOfPixelToCheck, 1];

                if (x >= 0 && x < pca.Width && y >= 0 && y <= pca.Height)
                {
                    if (pca.IsSolid(x, y)) //todo
                    {
                        next = new Vector2(x, y);
                        return(true);
                    }
                }
            }

            next = Vector2.Zero;
            return(false);
        }
        public static Vertices CreateVertices(uint[] data, int width, int height, bool holeDetection)
        {
            PolygonCreationAssistance pca = new PolygonCreationAssistance(data, width, height);

            pca.HoleDetection = holeDetection;
            List <Vertices> verts = CreateVertices(pca);

            return(verts[0]);
        }
 /// <summary>
 /// Detects the vertices of the supplied texture data.
 /// </summary>
 /// <param name="data">The texture data.</param>
 /// <param name="width">The texture width.</param>
 /// <param name="height">The texture height.</param>
 /// <param name="holeDetection">if set to <c>true</c> it will perform hole detection.</param>
 /// <param name="hullTolerance">The hull tolerance.</param>
 /// <param name="alphaTolerance">The alpha tolerance.</param>
 /// <param name="multiPartDetection">if set to <c>true</c> it will perform multi part detection.</param>
 /// <returns></returns>
 public static List<Vertices> DetectVertices(uint[] data, int width, int height, float hullTolerance,
                                             byte alphaTolerance, bool multiPartDetection, bool holeDetection)
 {
     PolygonCreationAssistance pca = new PolygonCreationAssistance(data, width, height);
     pca.HullTolerance = hullTolerance;
     pca.AlphaTolerance = alphaTolerance;
     pca.MultipartDetection = multiPartDetection;
     pca.HoleDetection = holeDetection;
     return DetectVertices(ref pca);
 }
        public static List <Vertices> CreateVertices(uint[] data, int width, int height, float hullTolerance,
                                                     byte alphaTolerance, bool multiPartDetection, bool holeDetection)
        {
            PolygonCreationAssistance pca = new PolygonCreationAssistance(data, width, height);

            pca.HullTolerance      = hullTolerance;
            pca.AlphaTolerance     = alphaTolerance;
            pca.MultipartDetection = multiPartDetection;
            pca.HoleDetection      = holeDetection;
            return(CreateVertices(pca));
        }
        private static bool DistanceToHullAcceptable(PolygonCreationAssistance pca, Vertices polygon, Vector2 point,
                                                     bool higherDetail)
        {
            if (polygon != null && polygon.Count > 2)
            {
                Vector2 edgeVertex2 = polygon[polygon.Count - 1];

                Vector2 edgeVertex1;
                if (higherDetail)
                {
                    for (int i = 0; i < polygon.Count; i++)
                    {
                        edgeVertex1 = polygon[i];

                        if (LineTools.DistanceBetweenPointAndLineSegment(ref point, ref edgeVertex1, ref edgeVertex2) <=
                            pca.HullTolerance ||
                            LineTools.DistanceBetweenPointAndPoint(ref point, ref edgeVertex1) <= pca.HullTolerance)
                        {
                            return(false);
                        }

                        edgeVertex2 = polygon[i];
                    }

                    return(true);
                }
                else
                {
                    for (int i = 0; i < polygon.Count; i++)
                    {
                        edgeVertex1 = polygon[i];

                        if (LineTools.DistanceBetweenPointAndLineSegment(ref point, ref edgeVertex1, ref edgeVertex2) <=
                            pca.HullTolerance)
                        {
                            return(false);
                        }

                        edgeVertex2 = polygon[i];
                    }

                    return(true);
                }
            }

            return(false);
        }
        private static bool IsNearPixel(PolygonCreationAssistance pca, Vector2 current, Vector2 near)
        {
            for (int i = 0; i < 8; i++)
            {
                int x = (int)current.X + ClosePixels[i, 0];
                int y = (int)current.Y + ClosePixels[i, 1];

                if (x >= 0 && x <= pca.Width && y >= 0 && y <= pca.Height)
                {
                    if (x == (int)near.X && y == (int)near.Y)
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        private static bool GetHullEntrance(PolygonCreationAssistance pca, out Vector2 entrance)
        {
            // Search for first solid pixel.
            for (int y = 0; y <= pca.Height; y++)
            {
                for (int x = 0; x <= pca.Width; x++)
                {
                    if (pca.IsSolid(x, y))
                    {
                        entrance = new Vector2(x, y);
                        return(true);
                    }
                }
            }

            // If there are no solid pixels.
            entrance = Vector2.Zero;
            return(false);
        }
Example #12
0
        private static bool SearchNearPixels(PolygonCreationAssistance pca, bool searchingForSolidPixel,
                                             ref Vector2 current, out Vector2 foundPixel)
        {
            for (int i = 0; i < 8; i++)
            {
                int x = (int)current.X + ClosePixels[i, 0];
                int y = (int)current.Y + ClosePixels[i, 1];

                if (!searchingForSolidPixel ^ pca.IsSolid(x, y))
                {
                    foundPixel = new Vector2(x, y);
                    return(true);
                }
            }

            // Nothing found.
            foundPixel = Vector2.Zero;
            return(false);
        }
        private static List<Vertices> DetectVertices(ref PolygonCreationAssistance pca)
        {
            List<Vertices> polygons = new List<Vertices>();

            Vertices polygon;
            Vertices holePolygon;

            Vector2? holeEntrance = null;
            Vector2? polygonEntrance = null;

            List<Vector2> blackList = new List<Vector2>();

            // Check the array you just got.
            Debug.Assert(pca.IsValid(),
                         "Sizes don't match: Color array must contain texture width * texture height elements.");

            bool searchOn;
            do
            {
                if (polygons.Count == 0)
                {
                    polygon = CreateSimplePolygon(pca, Vector2.Zero, Vector2.Zero);

                    if (polygon != null && polygon.Count > 2)
                    {
                        polygonEntrance = GetTopMostVertex(polygon);
                    }
                }
                else if (polygonEntrance.HasValue)
                {
                    polygon = CreateSimplePolygon(pca, polygonEntrance.Value,
                                                  new Vector2(polygonEntrance.Value.X - 1f, polygonEntrance.Value.Y));
                }
                else
                {
                    break;
                }

                searchOn = false;

                if (polygon != null && polygon.Count > 2)
                {
                    if (pca.HoleDetection)
                    {
                        do
                        {
                            holeEntrance = GetHoleHullEntrance(pca, polygon, holeEntrance);

                            if (holeEntrance.HasValue)
                            {
                                if (!blackList.Contains(holeEntrance.Value))
                                {
                                    blackList.Add(holeEntrance.Value);
                                    holePolygon = CreateSimplePolygon(pca, holeEntrance.Value,
                                                                      new Vector2(holeEntrance.Value.X + 1,
                                                                                  holeEntrance.Value.Y));

                                    if (holePolygon != null && holePolygon.Count > 2)
                                    {
                                        holePolygon.Add(holePolygon[0]);

                                        int vertex2Index;
                                        int vertex1Index;
                                        if (SplitPolygonEdge(polygon, EdgeAlignment.Vertical, holeEntrance.Value,
                                                             out vertex1Index, out vertex2Index))
                                        {
                                            polygon.InsertRange(vertex2Index, holePolygon);
                                        }
                                    }
                                }
                                else
                                {
                                    break;
                                }
                            }
                            else
                            {
                                break;
                            }
                        } while (true);
                    }

                    polygons.Add(polygon);

                    if (pca.MultipartDetection)
                    {
                        // 1:  95 / 151
                        // 2: 232 / 252
                        // 
                        while (GetNextHullEntrance(pca, polygonEntrance.Value, out polygonEntrance))
                        {
                            bool inPolygon = false;

                            for (int i = 0; i < polygons.Count; i++)
                            {
                                polygon = polygons[i];

                                if (InPolygon(pca, polygon, polygonEntrance.Value))
                                {
                                    inPolygon = true;
                                    break;
                                }
                            }

                            if (!inPolygon)
                            {
                                searchOn = true;
                                break;
                            }
                        }
                    }
                }
            } while (searchOn);

            return polygons;
        }
        private static bool IsNearPixel(PolygonCreationAssistance pca, ref Vector2 current, ref Vector2 near)
        {
            for (int i = 0; i < 8; i++)
            {
                int x = (int) current.X + ClosePixels[i, 0];
                int y = (int) current.Y + ClosePixels[i, 1];

                if (x >= 0 && x <= pca.Width && y >= 0 && y <= pca.Height)
                {
                    if (x == (int) near.X && y == (int) near.Y)
                    {
                        return true;
                    }
                }
            }

            return false;
        }
        private static bool GetHullEntrance(PolygonCreationAssistance pca, out Vector2 entrance)
        {
            // Search for first solid pixel.
            for (int y = 0; y <= pca.Height; y++)
            {
                for (int x = 0; x <= pca.Width; x++)
                {
                    if (pca.IsSolid(x, y))
                    {
                        entrance = new Vector2(x, y);
                        return true;
                    }
                }
            }

            // If there are no solid pixels.
            entrance = Vector2.Zero;
            return false;
        }
        private static List <Vertices> CreateVertices(PolygonCreationAssistance pca)
        {
            List <Vertices> polygons = new List <Vertices>();

            Vertices polygon;
            Vertices holePolygon;

            Vector2?holeEntrance    = null;
            Vector2?polygonEntrance = null;

            List <Vector2> blackList = new List <Vector2>();

            // First of all: Check the array you just got.
            if (pca.IsValid())
            {
                bool searchOn;
                do
                {
                    if (polygons.Count == 0)
                    {
                        polygon = CreateSimplePolygon(pca, Vector2.Zero, Vector2.Zero);

                        if (polygon != null && polygon.Count > 2)
                        {
                            polygonEntrance = GetTopMostVertex(polygon);
                        }
                    }
                    else if (polygonEntrance.HasValue)
                    {
                        polygon = CreateSimplePolygon(pca, polygonEntrance.Value,
                                                      new Vector2(polygonEntrance.Value.X - 1f, polygonEntrance.Value.Y));
                    }
                    else
                    {
                        break;
                    }

                    searchOn = false;

                    if (polygon != null && polygon.Count > 2)
                    {
                        if (pca.HoleDetection)
                        {
                            do
                            {
                                holeEntrance = GetHoleHullEntrance(pca, polygon, holeEntrance);

                                if (holeEntrance.HasValue)
                                {
                                    if (!blackList.Contains(holeEntrance.Value))
                                    {
                                        blackList.Add(holeEntrance.Value);
                                        holePolygon = CreateSimplePolygon(pca, holeEntrance.Value,
                                                                          new Vector2(holeEntrance.Value.X + 1,
                                                                                      holeEntrance.Value.Y));

                                        if (holePolygon != null && holePolygon.Count > 2)
                                        {
                                            holePolygon.Add(holePolygon[0]);

                                            int vertex2Index;
                                            int vertex1Index;
                                            if (SplitPolygonEdge(polygon, EdgeAlignment.Vertical, holeEntrance.Value,
                                                                 out vertex1Index, out vertex2Index))
                                            {
                                                polygon.InsertRange(vertex2Index, holePolygon);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                                else
                                {
                                    break;
                                }
                            } while (true);
                        }

                        polygons.Add(polygon);

                        if (pca.MultipartDetection)
                        {
                            // 1:  95 / 151
                            // 2: 232 / 252
                            //
                            while (GetNextHullEntrance(pca, polygonEntrance.Value, out polygonEntrance))
                            {
                                bool inPolygon = false;

                                for (int i = 0; i < polygons.Count; i++)
                                {
                                    polygon = polygons[i];

                                    if (InPolygon(pca, ref polygon, polygonEntrance.Value))
                                    {
                                        inPolygon = true;
                                        break;
                                    }
                                }

                                if (!inPolygon)
                                {
                                    searchOn = true;
                                    break;
                                }
                            }
                        }
                    }
                } while (searchOn);
            }
            else
            {
                throw new Exception(
                          "Sizes don't match: Color array must contain texture width * texture height elements.");
            }

            return(polygons);
        }
        private static bool SearchNearPixels(PolygonCreationAssistance pca, bool searchingForSolidPixel,
                                             ref Vector2 current, out Vector2 foundPixel)
        {
            for (int i = 0; i < 8; i++)
            {
                int x = (int) current.X + ClosePixels[i, 0];
                int y = (int) current.Y + ClosePixels[i, 1];

                if (!searchingForSolidPixel ^ pca.IsSolid(x, y))
                {
                    foundPixel = new Vector2(x, y);
                    return true;
                }
            }

            // Nothing found.
            foundPixel = Vector2.Zero;
            return false;
        }
        private static bool InPolygon(PolygonCreationAssistance pca, Vertices polygon, Vector2 point)
        {
            bool inPolygon = !DistanceToHullAcceptable(pca, polygon, point, true);

            if (!inPolygon)
            {
                List<CrossingEdgeInfo> edges = GetCrossingEdges(polygon, EdgeAlignment.Vertical, (int) point.Y);

                if (edges.Count > 0 && edges.Count%2 == 0)
                {
                    for (int i = 0; i < edges.Count; i += 2)
                    {
                        if (edges[i].CrossingPoint.X <= point.X && edges[i + 1].CrossingPoint.X >= point.X)
                        {
                            return true;
                        }
                    }

                    return false;
                }
                return false;
            }

            return true;
        }
        private static Vertices CreateSimplePolygon(PolygonCreationAssistance pca, Vector2 entrance, Vector2 last)
        {
            bool entranceFound = false;
            bool endOfHull = false;

            Vertices polygon = new Vertices(32);
            Vertices hullArea = new Vertices(32);
            Vertices endOfHullArea = new Vertices(32);

            Vector2 current = Vector2.Zero;

            #region Entrance check

            // Get the entrance point. //todo: alle möglichkeiten testen
            if (entrance == Vector2.Zero || !pca.InBounds(ref entrance))
            {
                entranceFound = GetHullEntrance(pca, out entrance);

                if (entranceFound)
                {
                    current = new Vector2(entrance.X - 1f, entrance.Y);
                }
            }
            else
            {
                if (pca.IsSolid(ref entrance))
                {
                    if (IsNearPixel(pca, ref entrance, ref last))
                    {
                        current = last;
                        entranceFound = true;
                    }
                    else
                    {
                        Vector2 temp;
                        if (SearchNearPixels(pca, false, ref entrance, out temp))
                        {
                            current = temp;
                            entranceFound = true;
                        }
                        else
                        {
                            entranceFound = false;
                        }
                    }
                }
            }

            #endregion

            if (entranceFound)
            {
                polygon.Add(entrance);
                hullArea.Add(entrance);

                Vector2 next = entrance;

                do
                {
                    // Search in the pre vision list for an outstanding point.
                    Vector2 outstanding;
                    if (SearchForOutstandingVertex(hullArea, pca.HullTolerance, out outstanding))
                    {
                        if (endOfHull)
                        {
                            // We have found the next pixel, but is it on the last bit of the hull?
                            if (endOfHullArea.Contains(outstanding))
                            {
                                // Indeed.
                                polygon.Add(outstanding);
                            }

                            // That's enough, quit.
                            break;
                        }

                        // Add it and remove all vertices that don't matter anymore
                        // (all the vertices before the outstanding).
                        polygon.Add(outstanding);
                        hullArea.RemoveRange(0, hullArea.IndexOf(outstanding));
                    }

                    // Last point gets current and current gets next. Our little spider is moving forward on the hull ;).
                    last = current;
                    current = next;

                    // Get the next point on hull.
                    if (GetNextHullPoint(pca, ref last, ref current, out next))
                    {
                        // Add the vertex to a hull pre vision list.
                        hullArea.Add(next);
                    }
                    else
                    {
                        // Quit
                        break;
                    }

                    if (next == entrance && !endOfHull)
                    {
                        // It's the last bit of the hull, search on and exit at next found vertex.
                        endOfHull = true;
                        endOfHullArea.AddRange(hullArea);
                    }
                } while (true);
            }

            return polygon;
        }
        private static bool DistanceToHullAcceptable(PolygonCreationAssistance pca, Vertices polygon, Vector2 point,
                                                     bool higherDetail)
        {
            if (polygon != null && polygon.Count > 2)
            {
                Vector2 edgeVertex2 = polygon[polygon.Count - 1];

                Vector2 edgeVertex1;
                if (higherDetail)
                {
                    for (int i = 0; i < polygon.Count; i++)
                    {
                        edgeVertex1 = polygon[i];

                        if (LineTools.DistanceBetweenPointAndLineSegment(ref point, ref edgeVertex1, ref edgeVertex2) <=
                            pca.HullTolerance ||
                            LineTools.DistanceBetweenPointAndPoint(ref point, ref edgeVertex1) <= pca.HullTolerance)
                        {
                            return false;
                        }

                        edgeVertex2 = polygon[i];
                    }

                    return true;
                }
                else
                {
                    for (int i = 0; i < polygon.Count; i++)
                    {
                        edgeVertex1 = polygon[i];

                        if (LineTools.DistanceBetweenPointAndLineSegment(ref point, ref edgeVertex1, ref edgeVertex2) <=
                            pca.HullTolerance)
                        {
                            return false;
                        }

                        edgeVertex2 = polygon[i];
                    }

                    return true;
                }
            }

            return false;
        }
        private static Vertices CreateSimplePolygon(PolygonCreationAssistance pca, Vector2 entrance, Vector2 last)
        {
            bool entranceFound = false;
            bool endOfHull     = false;

            Vertices polygon       = new Vertices();
            Vertices hullArea      = new Vertices();
            Vertices endOfHullArea = new Vertices();

            Vector2 current = Vector2.Zero;

            #region Entrance check

            // Get the entrance point. //todo: alle möglichkeiten testen
            if (entrance == Vector2.Zero || !pca.InBounds(entrance))
            {
                entranceFound = GetHullEntrance(pca, out entrance);

                if (entranceFound)
                {
                    current = new Vector2(entrance.X - 1f, entrance.Y);
                }
            }
            else
            {
                if (pca.IsSolid(entrance))
                {
                    if (IsNearPixel(pca, entrance, last))
                    {
                        current       = last;
                        entranceFound = true;
                    }
                    else
                    {
                        Vector2 temp;
                        if (SearchNearPixels(pca, false, entrance, out temp))
                        {
                            current       = temp;
                            entranceFound = true;
                        }
                        else
                        {
                            entranceFound = false;
                        }
                    }
                }
            }

            #endregion

            if (entranceFound)
            {
                polygon.Add(entrance);
                hullArea.Add(entrance);

                Vector2 next = entrance;

                do
                {
                    // Search in the pre vision list for an outstanding point.
                    Vector2 outstanding;
                    if (SearchForOutstandingVertex(hullArea, pca.HullTolerance, out outstanding))
                    {
                        if (endOfHull)
                        {
                            // We have found the next pixel, but is it on the last bit of the hull?
                            if (endOfHullArea.Contains(outstanding))
                            {
                                // Indeed.
                                polygon.Add(outstanding);
                            }

                            // That's enough, quit.
                            break;
                        }

                        // Add it and remove all vertices that don't matter anymore
                        // (all the vertices before the outstanding).
                        polygon.Add(outstanding);
                        hullArea.RemoveRange(0, hullArea.IndexOf(outstanding));
                    }

                    // Last point gets current and current gets next. Our little spider is moving forward on the hull ;).
                    last    = current;
                    current = next;

                    // Get the next point on hull.
                    if (GetNextHullPoint(pca, ref last, ref current, out next))
                    {
                        // Add the vertex to a hull pre vision list.
                        hullArea.Add(next);
                    }
                    else
                    {
                        // Quit
                        break;
                    }

                    if (next == entrance && !endOfHull)
                    {
                        // It's the last bit of the hull, search on and exit at next found vertex.
                        endOfHull = true;
                        endOfHullArea.AddRange(hullArea);
                    }
                } while (true);
            }

            return(polygon);
        }
        private static Vector2? GetHoleHullEntrance(PolygonCreationAssistance pca, Vertices polygon,
                                                    Vector2? startVertex)
        {
            List<CrossingEdgeInfo> edges;
            Vector2? entrance;

            int startLine;
            int endLine;

            int lastSolid = 0;
            bool foundSolid;
            bool foundTransparent;

            if (polygon != null && polygon.Count > 0)
            {
                if (startVertex.HasValue)
                {
                    startLine = (int) startVertex.Value.Y;
                }
                else
                {
                    startLine = (int) GetTopMostCoord(polygon);
                }
                endLine = (int) GetBottomMostCoord(polygon);

                if (startLine > 0 && startLine < pca.Height && endLine > 0 && endLine < pca.Height)
                {
                    // go from top to bottom of the polygon
                    for (int y = startLine; y <= endLine; y += pca.HoleDetectionLineStepSize)
                    {
                        // get x-coord of every polygon edge which crosses y
                        edges = GetCrossingEdges(polygon, EdgeAlignment.Vertical, y);

                        // we need an even number of crossing edges
                        if (edges.Count > 1 && edges.Count%2 == 0)
                        {
                            for (int i = 0; i < edges.Count; i += 2)
                            {
                                foundSolid = false;
                                foundTransparent = false;

                                for (int x = (int) edges[i].CrossingPoint.X;
                                     x <= (int) edges[i + 1].CrossingPoint.X;
                                     x++)
                                {
                                    if (pca.IsSolid(x, y))
                                    {
                                        if (!foundTransparent)
                                        {
                                            foundSolid = true;
                                            lastSolid = x;
                                        }

                                        if (foundSolid && foundTransparent)
                                        {
                                            entrance = new Vector2(lastSolid, y);

                                            if (DistanceToHullAcceptable(pca, polygon, entrance.Value, true))
                                            {
                                                return entrance;
                                            }
                                            entrance = null;
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        if (foundSolid)
                                        {
                                            foundTransparent = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return null;
        }
        private static Vector2?GetHoleHullEntrance(PolygonCreationAssistance pca, Vertices polygon,
                                                   Vector2?startVertex)
        {
            List <CrossingEdgeInfo> edges = new List <CrossingEdgeInfo>();
            Vector2?entrance;

            int startLine;
            int endLine;

            int  lastSolid = 0;
            bool foundSolid;
            bool foundTransparent;

            if (polygon != null && polygon.Count > 0)
            {
                if (startVertex.HasValue)
                {
                    startLine = (int)startVertex.Value.Y;
                }
                else
                {
                    startLine = (int)GetTopMostCoord(polygon);
                }
                endLine = (int)GetBottomMostCoord(polygon);

                if (startLine > 0 && startLine < pca.Height && endLine > 0 && endLine < pca.Height)
                {
                    // go from top to bottom of the polygon
                    for (int y = startLine; y <= endLine; y += pca.HoleDetectionLineStepSize)
                    {
                        // get x-coord of every polygon edge which crosses y
                        edges = GetCrossingEdges(polygon, EdgeAlignment.Vertical, y);

                        // we need an even number of crossing edges
                        if (edges.Count > 1 && edges.Count % 2 == 0)
                        {
                            for (int i = 0; i < edges.Count; i += 2)
                            {
                                foundSolid       = false;
                                foundTransparent = false;

                                for (int x = (int)edges[i].CrossingPoint.X;
                                     x <= (int)edges[i + 1].CrossingPoint.X;
                                     x++)
                                {
                                    if (pca.IsSolid(x, y))
                                    {
                                        if (!foundTransparent)
                                        {
                                            foundSolid = true;
                                            lastSolid  = x;
                                        }

                                        if (foundSolid && foundTransparent)
                                        {
                                            entrance = new Vector2(lastSolid, y);

                                            if (DistanceToHullAcceptable(pca, polygon, entrance.Value, true))
                                            {
                                                return(entrance);
                                            }
                                            entrance = null;
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        if (foundSolid)
                                        {
                                            foundTransparent = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(null);
        }
        private static bool GetNextHullEntrance(PolygonCreationAssistance pca, Vector2 start, out Vector2? entrance)
        {
            // Search for first solid pixel.
            int size = pca.Height*pca.Width;
            int x;

            bool foundTransparent = false;

            for (int i = (int) start.X + (int) start.Y*pca.Width; i <= size; i++)
            {
                if (pca.IsSolid(i))
                {
                    if (foundTransparent)
                    {
                        x = i%pca.Width;

                        entrance = new Vector2(x, (i - x)/(float) pca.Width);
                        return true;
                    }
                }
                else
                {
                    foundTransparent = true;
                }
            }

            // If there are no solid pixels.
            entrance = null;
            return false;
        }
        private static bool GetNextHullPoint(PolygonCreationAssistance pca, ref Vector2 last, ref Vector2 current,
                                             out Vector2 next)
        {
            int x;
            int y;

            int indexOfFirstPixelToCheck = GetIndexOfFirstPixelToCheck(ref last, ref current);
            int indexOfPixelToCheck;

            const int pixelsToCheck = 8; // _closePixels.Length;

            for (int i = 0; i < pixelsToCheck; i++)
            {
                indexOfPixelToCheck = (indexOfFirstPixelToCheck + i)%pixelsToCheck;

                x = (int) current.X + ClosePixels[indexOfPixelToCheck, 0];
                y = (int) current.Y + ClosePixels[indexOfPixelToCheck, 1];

                if (x >= 0 && x < pca.Width && y >= 0 && y <= pca.Height)
                {
                    if (pca.IsSolid(x, y)) //todo
                    {
                        next = new Vector2(x, y);
                        return true;
                    }
                }
            }

            next = Vector2.Zero;
            return false;
        }
        public static Vertices CreateVertices(uint[] data, int width, int height, bool holeDetection)
        {
            PolygonCreationAssistance pca = new PolygonCreationAssistance(data, width, height);
            pca.HoleDetection = holeDetection;
            List<Vertices> verts = CreateVertices(pca);

            return verts[0];
        }