// Double the width and height of a binarized image
    public static BinarizedImage ResizeImage( BinarizedImage a_rBinarizedImage )
    {
        int iImageHeight = a_rBinarizedImage.Height;
        int iImageWidth  = a_rBinarizedImage.Width;

        int iResizedImageHeight = 2 * iImageHeight;
        int iResizedImageWidth  = 2 * iImageWidth;

        BinarizedImage oResizedBinarizedImage = new BinarizedImage( iResizedImageWidth, iResizedImageHeight, false );

        // Upsampling
        // Copy original pixels to resized sprite pixels array
        for( int iResizedY = 0; iResizedY < iResizedImageHeight; ++iResizedY )
        {
            for( int iResizedX = 0; iResizedX < iResizedImageWidth; ++iResizedX )
            {
                // Euclidian div
                int iOriginalX = iResizedX / 2;
                int iOriginalY = iResizedY / 2;
                int iOriginalIndex = a_rBinarizedImage.Coords2PixelIndex( iOriginalX, iOriginalY );
                int iResizedIndex = oResizedBinarizedImage.Coords2PixelIndex( iResizedX, iResizedY );

                // Pixel copy
                oResizedBinarizedImage[ iResizedIndex ] = a_rBinarizedImage[ iOriginalIndex ];
            }
        }

        return oResizedBinarizedImage;
    }
    // Double the width and height of a binarized image
    public static BinarizedImage ResizeImage(BinarizedImage a_rBinarizedImage)
    {
        int iImageHeight = a_rBinarizedImage.Height;
        int iImageWidth  = a_rBinarizedImage.Width;

        int iResizedImageHeight = 2 * iImageHeight;
        int iResizedImageWidth  = 2 * iImageWidth;

        BinarizedImage oResizedBinarizedImage = new BinarizedImage(iResizedImageWidth, iResizedImageHeight, false);

        // Upsampling
        // Copy original pixels to resized sprite pixels array
        for (int iResizedY = 0; iResizedY < iResizedImageHeight; ++iResizedY)
        {
            for (int iResizedX = 0; iResizedX < iResizedImageWidth; ++iResizedX)
            {
                // Euclidian div
                int iOriginalX     = iResizedX / 2;
                int iOriginalY     = iResizedY / 2;
                int iOriginalIndex = a_rBinarizedImage.Coords2PixelIndex(iOriginalX, iOriginalY);
                int iResizedIndex  = oResizedBinarizedImage.Coords2PixelIndex(iResizedX, iResizedY);

                // Pixel copy
                oResizedBinarizedImage[iResizedIndex] = a_rBinarizedImage[iOriginalIndex];
            }
        }

        return(oResizedBinarizedImage);
    }
    public static BinarizedImage DownScaleImage(BinarizedImage a_rBinarizedImage, int a_iHorizontalSubDivs, int a_iVerticalSubDivs)
    {
        int iImageWidth  = a_rBinarizedImage.Width;
        int iImageHeight = a_rBinarizedImage.Height;

        int iGridSubDivPerRow    = (a_iVerticalSubDivs + 1);
        int iGridSubDivPerColumn = (a_iHorizontalSubDivs + 1);
        int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

        // Grid div dimensions
        float fGridSubDivPixelWidth  = ((float)iImageWidth) / (float)iGridSubDivPerRow;
        float fGridSubDivPixelHeight = ((float)iImageHeight) / (float)iGridSubDivPerColumn;

        BinarizedImage oResizedBinarizedImage = new BinarizedImage(iGridSubDivPerRow, iGridSubDivPerColumn, false);

        // Iterate grid divs
        for (int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex)
        {
            // ( X, Y ) grid div pos from grid div index (in grid div space)
            int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
            int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;

            // Compute the pixel bounds for this subdivision
            int iStartX = Mathf.FloorToInt(iGridSubDivX * fGridSubDivPixelWidth);
            int iStartY = Mathf.FloorToInt(iGridSubDivY * fGridSubDivPixelHeight);

            int iEndX = Mathf.CeilToInt((iGridSubDivX + 1) * fGridSubDivPixelWidth);
            int iEndY = Mathf.CeilToInt((iGridSubDivY + 1) * fGridSubDivPixelHeight);

            int iWidth  = iEndX - iStartX;
            int iHeight = iEndY - iStartY;

            // Set grid sub div as empty while no significant pixel found
            bool  bEmptyGridSubDiv     = true;
            float fGridSubDivAlphaMean = 0.0f;
            for (int iY = 0; iY < iHeight; ++iY)
            {
                for (int iX = 0; iX < iWidth; ++iX)
                {
                    int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex(iX + iStartX, iY + iStartY);

                    // At least one pixel is significant => need to create the whole grid div
                    if (a_rBinarizedImage[iPixelIndex])
                    {
                        bEmptyGridSubDiv = false;
                    }
                    fGridSubDivAlphaMean += a_rBinarizedImage.GetAlphaValue(iPixelIndex);
                }
            }
            fGridSubDivAlphaMean /= iHeight * iWidth;

            int iResizedIndex = oResizedBinarizedImage.Coords2PixelIndex(iGridSubDivX, iGridSubDivY);
            oResizedBinarizedImage.SetInternalPixel(iResizedIndex, !bEmptyGridSubDiv, fGridSubDivAlphaMean);
        }

        return(oResizedBinarizedImage);
    }
Пример #4
0
    // Polygonizes a flat parametrized grid mesh from a binarized image
    public static Mesh PolygonizeGrid(BinarizedImage a_rBinarizedImage, Vector2 a_f2Scale, Vector3 a_f3PivotPoint, int a_iHorizontalSubDivs, int a_iVerticalSubDivs, bool a_bExtrude = false, float a_fExtrusionDepth = 0.0f)
    {
        BinarizedImage oDownScaledBinarizedImage = Uni2DEditorShapeExtractionUtils.DownScaleImage(a_rBinarizedImage, a_iHorizontalSubDivs, a_iVerticalSubDivs);

        Mesh oGridPlaneMesh = new Mesh( );

        // Texture dimensions
        int iTextureWidth  = a_rBinarizedImage.Width;
        int iTextureHeight = a_rBinarizedImage.Height;

        int iGridSubDivPerRow    = (a_iVerticalSubDivs + 1);
        int iGridSubDivPerColumn = (a_iHorizontalSubDivs + 1);
        int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

        int iGridVertexCount = (a_iHorizontalSubDivs + 2) * (a_iVerticalSubDivs + 2);

        // Grid div dimensions
        float fGridSubDivPixelWidth  = ((float)iTextureWidth) / (float)iGridSubDivPerRow;
        float fGridSubDivPixelHeight = ((float)iTextureHeight) / (float)iGridSubDivPerColumn;

        // Vertex pos -> index dictionary: stores the index associated to a given vertex
        int iCurrentVertexIndex = 0;
        Dictionary <Vector3, int> oVertexPosIndexDict = new Dictionary <Vector3, int>(iGridVertexCount);

        // Mesh data
        List <int> oGridPlaneTriangleList = new List <int>(6 * iGridVertexCount);               // quad = 2 tris, 1 tri = 3 vertices => 2 * 3
        //List<Vector2> oGridPlaneUVList   = new List<Vector2>( iGridVertexCount );

        List <int> oOutlineSides = new List <int>();

        for (int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex)
        {
            int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
            int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;
            int iIndex       = oDownScaledBinarizedImage.Coords2PixelIndex(iGridSubDivX, iGridSubDivY);
            if (oDownScaledBinarizedImage[iIndex])
            {
                // ( X, Y ) grid div pos from grid div index (in grid div space)

                /*
                 * Grid div
                 *
                 *	       ...       ...
                 *	        |         |
                 *	... --- TL ------- TR --- ...
                 *	        |   T2  / |
                 *	        |     /   |
                 *	        |   /     |
                 *	        | /   T1  |
                 *	... --- BL ------- BR --- ...
                 *          |         |
                 *         ...       ...
                 */
                Vector3 f3BottomLeftVertex  = new Vector3(iGridSubDivX * fGridSubDivPixelWidth, iGridSubDivY * fGridSubDivPixelHeight, 0.0f);                                   // BL
                Vector3 f3BottomRightVertex = new Vector3((iGridSubDivX + 1) * fGridSubDivPixelWidth, iGridSubDivY * fGridSubDivPixelHeight, 0.0f);                             // BR
                Vector3 f3TopRightVertex    = new Vector3((iGridSubDivX + 1) * fGridSubDivPixelWidth, (iGridSubDivY + 1) * fGridSubDivPixelHeight, 0.0f);                       // TR
                Vector3 f3TopLeftVertex     = new Vector3(iGridSubDivX * fGridSubDivPixelWidth, (iGridSubDivY + 1) * fGridSubDivPixelHeight, 0.0f);                             // TL

                int iBottomLeftVertexIndex;
                int iBottomRightVertexIndex;
                int iTopRightVertexIndex;
                int iTopLeftVertexIndex;

                // For each grid div vertex, query its index if already created (the vertex might be shared with other grid divs).
                // If not, create it.
                if (oVertexPosIndexDict.TryGetValue(f3BottomLeftVertex, out iBottomLeftVertexIndex) == false)
                {
                    iBottomLeftVertexIndex = iCurrentVertexIndex++;
                    oVertexPosIndexDict.Add(f3BottomLeftVertex, iBottomLeftVertexIndex);
                    //oGridPlaneUVList.Add( new Vector2( f3BottomLeftVertex.x / (float) iTextureWidth, f3BottomLeftVertex.y / (float) iTextureHeight ) );
                }

                if (oVertexPosIndexDict.TryGetValue(f3BottomRightVertex, out iBottomRightVertexIndex) == false)
                {
                    iBottomRightVertexIndex = iCurrentVertexIndex++;
                    oVertexPosIndexDict.Add(f3BottomRightVertex, iBottomRightVertexIndex);
                    //oGridPlaneUVList.Add( new Vector2( f3BottomRightVertex.x / (float) iTextureWidth, f3BottomRightVertex.y / (float) iTextureHeight ) );
                }

                if (oVertexPosIndexDict.TryGetValue(f3TopRightVertex, out iTopRightVertexIndex) == false)
                {
                    iTopRightVertexIndex = iCurrentVertexIndex++;
                    oVertexPosIndexDict.Add(f3TopRightVertex, iTopRightVertexIndex);
                    //oGridPlaneUVList.Add( new Vector2( f3TopRightVertex.x / (float) iTextureWidth, f3TopRightVertex.y / (float) iTextureHeight ) );
                }

                if (oVertexPosIndexDict.TryGetValue(f3TopLeftVertex, out iTopLeftVertexIndex) == false)
                {
                    iTopLeftVertexIndex = iCurrentVertexIndex++;
                    oVertexPosIndexDict.Add(f3TopLeftVertex, iTopLeftVertexIndex);
                    //oGridPlaneUVList.Add( new Vector2( f3TopLeftVertex.x / (float) iTextureWidth, f3TopLeftVertex.y / (float) iTextureHeight ) );
                }

                // Create grid div triangles
                // First triangle (T1)
                oGridPlaneTriangleList.Add(iBottomRightVertexIndex);                    // BR
                oGridPlaneTriangleList.Add(iBottomLeftVertexIndex);                     // BL
                oGridPlaneTriangleList.Add(iTopRightVertexIndex);                       // TR

                // Second triangle (T2)
                oGridPlaneTriangleList.Add(iBottomLeftVertexIndex);                     // BL
                oGridPlaneTriangleList.Add(iTopLeftVertexIndex);                        // TL
                oGridPlaneTriangleList.Add(iTopRightVertexIndex);                       // TR

                // If we extrude check the sides to see if there are parts of the outline
                if (a_bExtrude)
                {
                    // Up
                    if (IsBinarizedImageCellEmpty(iGridSubDivX, iGridSubDivY + 1, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
                    {
                        oOutlineSides.Add(iTopLeftVertexIndex);
                        oOutlineSides.Add(iTopRightVertexIndex);
                    }

                    // Right
                    if (IsBinarizedImageCellEmpty(iGridSubDivX + 1, iGridSubDivY, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
                    {
                        oOutlineSides.Add(iTopRightVertexIndex);
                        oOutlineSides.Add(iBottomRightVertexIndex);
                    }

                    // Bottom
                    if (IsBinarizedImageCellEmpty(iGridSubDivX, iGridSubDivY - 1, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
                    {
                        oOutlineSides.Add(iBottomRightVertexIndex);
                        oOutlineSides.Add(iBottomLeftVertexIndex);
                    }

                    // Left
                    if (IsBinarizedImageCellEmpty(iGridSubDivX - 1, iGridSubDivY, iGridSubDivPerRow, iGridSubDivPerColumn, oDownScaledBinarizedImage))
                    {
                        oOutlineSides.Add(iBottomLeftVertexIndex);
                        oOutlineSides.Add(iTopLeftVertexIndex);
                    }
                }
            }
        }

        // Apply pivot + compute UVs
        int     i2DVertexCount  = oVertexPosIndexDict.Count;
        int     iVertexCount    = i2DVertexCount;
        Vector3 f3ExtrudeOffset = Vector3.zero;

        if (a_bExtrude)
        {
            iVertexCount    = 2 * iVertexCount;
            f3ExtrudeOffset = Vector3.forward * a_fExtrusionDepth * 0.5f;
        }
        Vector2[] oGridPlaneUVs      = new Vector2[iVertexCount];
        Vector3[] oGridPlaneVertices = new Vector3[iVertexCount];
        Vector2   f2Dimensions       = new Vector2(1.0f / (float)iTextureWidth, 1.0f / (float)iTextureHeight);

        foreach (KeyValuePair <Vector3, int> rVertexPosIndexPair in oVertexPosIndexDict)
        {
            Vector3 f3VertexPos = rVertexPosIndexPair.Key;
            int     iIndex      = rVertexPosIndexPair.Value;

            Vector2 f2UV     = new Vector2(f3VertexPos.x * f2Dimensions.x, f3VertexPos.y * f2Dimensions.y);
            Vector3 f3Vertex = (f3VertexPos - a_f3PivotPoint);
            f3Vertex.x *= a_f2Scale.x;
            f3Vertex.y *= a_f2Scale.y;

            if (a_bExtrude)
            {
                oGridPlaneUVs[iIndex]      = f2UV;
                oGridPlaneVertices[iIndex] = f3Vertex - f3ExtrudeOffset;

                oGridPlaneUVs[iIndex + i2DVertexCount]      = f2UV;
                oGridPlaneVertices[iIndex + i2DVertexCount] = f3Vertex + f3ExtrudeOffset;
            }
            else
            {
                oGridPlaneUVs[iIndex]      = f2UV;
                oGridPlaneVertices[iIndex] = f3Vertex;
            }
        }


        // Surface Triangles
        int i2DTriangleIndicesCount      = oGridPlaneTriangleList.Count;
        int iSurfaceTriangleIndicesCount = oGridPlaneTriangleList.Count;
        int iTriangleIndicesCount        = oGridPlaneTriangleList.Count;

        if (a_bExtrude)
        {
            iSurfaceTriangleIndicesCount *= 2;
            iTriangleIndicesCount         = 2 * iTriangleIndicesCount + oOutlineSides.Count * 3;
        }
        int[] oGridPlaneTriangleIndices = new int[iTriangleIndicesCount];
        for (int i = 0; i < i2DTriangleIndicesCount; ++i)
        {
            int iIndex = oGridPlaneTriangleList[i];
            oGridPlaneTriangleIndices[i] = iIndex;
            if (a_bExtrude)
            {
                oGridPlaneTriangleIndices[iSurfaceTriangleIndicesCount - 1 - i] = iIndex + i2DVertexCount;
            }
        }

        // Side Triangles
        if (a_bExtrude)
        {
            int iStartSideTriangle = iSurfaceTriangleIndicesCount;
            int iOutlineSideCount  = oOutlineSides.Count / 2;
            for (int i = 0; i < iOutlineSideCount; ++i)
            {
                int iSideFrontBegin = oOutlineSides[i * 2];
                int iSideFrontEnd   = oOutlineSides[i * 2 + 1];
                int iSideBackEnd    = iSideFrontEnd + i2DVertexCount;
                int iSideBackBegin  = iSideFrontBegin + i2DVertexCount;

                int iSideQuadStartIndex = iStartSideTriangle + i * 6;

                oGridPlaneTriangleIndices[iSideQuadStartIndex + 0] = iSideFrontBegin;
                oGridPlaneTriangleIndices[iSideQuadStartIndex + 1] = iSideBackBegin;
                oGridPlaneTriangleIndices[iSideQuadStartIndex + 2] = iSideBackEnd;

                oGridPlaneTriangleIndices[iSideQuadStartIndex + 3] = iSideFrontBegin;
                oGridPlaneTriangleIndices[iSideQuadStartIndex + 4] = iSideBackEnd;
                oGridPlaneTriangleIndices[iSideQuadStartIndex + 5] = iSideFrontEnd;
            }
        }

        oGridPlaneMesh.vertices  = oGridPlaneVertices;
        oGridPlaneMesh.uv        = oGridPlaneUVs;
        oGridPlaneMesh.triangles = oGridPlaneTriangleIndices;           // LINQ

        return(oGridPlaneMesh);
    }
Пример #5
0
    // Polygonizes a flat parametrized grid mesh from a binarized image
    public static Mesh PolygonizeGrid(BinarizedImage a_rBinarizedImage, float a_fScale, Vector3 a_f3PivotPoint, int a_iHorizontalSubDivs, int a_iVerticalSubDivs)
    {
        Mesh oGridPlaneMesh = new Mesh( );

        // Texture dimensions
        int iTextureWidth  = a_rBinarizedImage.Width;
        int iTextureHeight = a_rBinarizedImage.Height;

        int iGridSubDivPerRow    = (a_iVerticalSubDivs + 1);
        int iGridSubDivPerColumn = (a_iHorizontalSubDivs + 1);
        int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

        int iGridVertexCount = (a_iHorizontalSubDivs + 2) * (a_iVerticalSubDivs + 2);

        // Grid div dimensions
        float fGridSubDivPixelWidth  = ((float)iTextureWidth) / (float)iGridSubDivPerRow;
        float fGridSubDivPixelHeight = ((float)iTextureHeight) / (float)iGridSubDivPerColumn;

        // Also store these values as ints
        int iGridSubDivPixelWidth  = Mathf.FloorToInt(fGridSubDivPixelWidth);
        int iGridSubDivPixelHeight = Mathf.FloorToInt(fGridSubDivPixelHeight);

        // Vertex pos -> index dictionary: stores the index associated to a given vertex
        int iCurrentVertexIndex = 0;
        Dictionary <Vector3, int> oVertexPosIndexDict = new Dictionary <Vector3, int>(iGridVertexCount);

        // Mesh data
        List <int> oGridPlaneTriangleList = new List <int>(6 * iGridVertexCount);               // quad = 2 tris, 1 tri = 3 vertices => 2 * 3

        //List<Vector2> oGridPlaneUVList   = new List<Vector2>( iGridVertexCount );

        // Iterate grid divs
        for (int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex)
        {
            // ( X, Y ) grid div pos from grid div index (in grid div space)
            int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
            int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;

            // Set grid sub div as empty while no significant pixel found
            bool bEmptyGridSubDiv = true;
            for (int iY = 0, iYLimit = iGridSubDivPixelHeight; bEmptyGridSubDiv && iY < iYLimit; ++iY)
            {
                for (int iX = 0, iXLimit = iGridSubDivPixelWidth; bEmptyGridSubDiv && iX < iXLimit; ++iX)
                {
                    int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex(iX + Mathf.FloorToInt(iGridSubDivX * fGridSubDivPixelWidth), iY + Mathf.FloorToInt(iGridSubDivY * fGridSubDivPixelHeight));
                    //int iPixelIndex = Mathf.FloorToInt( iGridSubDivY * fGridSubDivPixelHeight ) * iTextureWidth + Mathf.FloorToInt( iGridSubDivX * fGridSubDivPixelWidth ) + iY * iTextureWidth + iX;
                    //bEmptyGridSubDiv = a_rBinarizedImage[ iPixelIndex ];

                    // At least one pixel is significant => need to create the whole grid div
                    if (a_rBinarizedImage[iPixelIndex])
                    {
                        /*
                         * Grid div
                         *
                         *	       ...       ...
                         *	        |         |
                         *	... --- TL ------- TR --- ...
                         *	        |   T2  / |
                         *	        |     /   |
                         *	        |   /     |
                         *	        | /   T1  |
                         *	... --- BL ------- BR --- ...
                         *          |         |
                         *         ...       ...
                         */
                        Vector3 f3BottomLeftVertex  = new Vector3(iGridSubDivX * fGridSubDivPixelWidth, iGridSubDivY * fGridSubDivPixelHeight, 0.0f);                                           // BL
                        Vector3 f3BottomRightVertex = new Vector3((iGridSubDivX + 1) * fGridSubDivPixelWidth, iGridSubDivY * fGridSubDivPixelHeight, 0.0f);                                     // BR
                        Vector3 f3TopRightVertex    = new Vector3((iGridSubDivX + 1) * fGridSubDivPixelWidth, (iGridSubDivY + 1) * fGridSubDivPixelHeight, 0.0f);                               // TR
                        Vector3 f3TopLeftVertex     = new Vector3(iGridSubDivX * fGridSubDivPixelWidth, (iGridSubDivY + 1) * fGridSubDivPixelHeight, 0.0f);                                     // TL

                        int iBottomLeftVertexIndex;
                        int iBottomRightVertexIndex;
                        int iTopRightVertexIndex;
                        int iTopLeftVertexIndex;

                        // For each grid div vertex, query its index if already created (the vertex might be shared with other grid divs).
                        // If not, create it.
                        if (oVertexPosIndexDict.TryGetValue(f3BottomLeftVertex, out iBottomLeftVertexIndex) == false)
                        {
                            iBottomLeftVertexIndex = iCurrentVertexIndex++;
                            oVertexPosIndexDict.Add(f3BottomLeftVertex, iBottomLeftVertexIndex);
                            //oGridPlaneUVList.Add( new Vector2( f3BottomLeftVertex.x / (float) iTextureWidth, f3BottomLeftVertex.y / (float) iTextureHeight ) );
                        }

                        if (oVertexPosIndexDict.TryGetValue(f3BottomRightVertex, out iBottomRightVertexIndex) == false)
                        {
                            iBottomRightVertexIndex = iCurrentVertexIndex++;
                            oVertexPosIndexDict.Add(f3BottomRightVertex, iBottomRightVertexIndex);
                            //oGridPlaneUVList.Add( new Vector2( f3BottomRightVertex.x / (float) iTextureWidth, f3BottomRightVertex.y / (float) iTextureHeight ) );
                        }

                        if (oVertexPosIndexDict.TryGetValue(f3TopRightVertex, out iTopRightVertexIndex) == false)
                        {
                            iTopRightVertexIndex = iCurrentVertexIndex++;
                            oVertexPosIndexDict.Add(f3TopRightVertex, iTopRightVertexIndex);
                            //oGridPlaneUVList.Add( new Vector2( f3TopRightVertex.x / (float) iTextureWidth, f3TopRightVertex.y / (float) iTextureHeight ) );
                        }

                        if (oVertexPosIndexDict.TryGetValue(f3TopLeftVertex, out iTopLeftVertexIndex) == false)
                        {
                            iTopLeftVertexIndex = iCurrentVertexIndex++;
                            oVertexPosIndexDict.Add(f3TopLeftVertex, iTopLeftVertexIndex);
                            //oGridPlaneUVList.Add( new Vector2( f3TopLeftVertex.x / (float) iTextureWidth, f3TopLeftVertex.y / (float) iTextureHeight ) );
                        }

                        // Create grid div triangles
                        // First triangle (T1)
                        oGridPlaneTriangleList.Add(iBottomRightVertexIndex);                            // BR
                        oGridPlaneTriangleList.Add(iBottomLeftVertexIndex);                             // BL
                        oGridPlaneTriangleList.Add(iTopRightVertexIndex);                               // TR

                        // Second triangle (T2)
                        oGridPlaneTriangleList.Add(iBottomLeftVertexIndex);                             // BL
                        oGridPlaneTriangleList.Add(iTopLeftVertexIndex);                                // TL
                        oGridPlaneTriangleList.Add(iTopRightVertexIndex);                               // TR

                        // Set grid sub div as not empty
                        bEmptyGridSubDiv = false;
                    }
                }
            }
        }

        // Apply pivot + compute UVs
        int iVertexCount = oVertexPosIndexDict.Count;

        Vector2[] oGridPlaneUVs      = new Vector2[iVertexCount];
        Vector3[] oGridPlaneVertices = new Vector3[iVertexCount];
        Vector2   f2Dimensions       = new Vector2(1.0f / (float)iTextureWidth, 1.0f / (float)iTextureHeight);

        foreach (KeyValuePair <Vector3, int> rVertexPosIndexPair in oVertexPosIndexDict)
        {
            Vector3 f3VertexPos = rVertexPosIndexPair.Key;
            int     iIndex      = rVertexPosIndexPair.Value;

            oGridPlaneUVs[iIndex]      = new Vector2(f3VertexPos.x * f2Dimensions.x, f3VertexPos.y * f2Dimensions.y);
            oGridPlaneVertices[iIndex] = (f3VertexPos - a_f3PivotPoint) * a_fScale;
        }

        oGridPlaneMesh.vertices  = oGridPlaneVertices;
        oGridPlaneMesh.uv        = oGridPlaneUVs;
        oGridPlaneMesh.triangles = oGridPlaneTriangleList.ToArray( );           // LINQ

        return(oGridPlaneMesh);
    }
	// Polygonizes a flat parametrized grid mesh from a binarized image
	public static Mesh PolygonizeGrid( BinarizedImage a_rBinarizedImage, float a_fScale, Vector3 a_f3PivotPoint, int a_iHorizontalSubDivs, int a_iVerticalSubDivs )
	{
		Mesh oGridPlaneMesh = new Mesh( );

		// Texture dimensions
		int iTextureWidth  = a_rBinarizedImage.Width;
		int iTextureHeight = a_rBinarizedImage.Height;

		int iGridSubDivPerRow    = ( a_iVerticalSubDivs + 1 );
		int iGridSubDivPerColumn = ( a_iHorizontalSubDivs + 1 );
		int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;

		int iGridVertexCount = ( a_iHorizontalSubDivs + 2 ) * ( a_iVerticalSubDivs + 2 );
		
		// Grid div dimensions
		float fGridSubDivPixelWidth  = ( (float) iTextureWidth  ) / (float) iGridSubDivPerRow;
		float fGridSubDivPixelHeight = ( (float) iTextureHeight ) / (float) iGridSubDivPerColumn;

		// Vertex pos -> index dictionary: stores the index associated to a given vertex
		int iCurrentVertexIndex = 0;
		Dictionary<Vector3,int> oVertexPosIndexDict = new Dictionary<Vector3, int>( iGridVertexCount );

		// Mesh data
		List<int> oGridPlaneTriangleList = new List<int>( 6 * iGridVertexCount );	// quad = 2 tris, 1 tri = 3 vertices => 2 * 3
		//List<Vector2> oGridPlaneUVList   = new List<Vector2>( iGridVertexCount );

		// Iterate grid divs
		for( int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex )
		{
			// ( X, Y ) grid div pos from grid div index (in grid div space)
			int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
			int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;
			
			// Compute the pixel bounds for this subdivision
			int iStartX = Mathf.FloorToInt(iGridSubDivX * fGridSubDivPixelWidth);
			int iStartY = Mathf.FloorToInt(iGridSubDivY * fGridSubDivPixelHeight);
			
			int iEndX = Mathf.CeilToInt((iGridSubDivX + 1) * fGridSubDivPixelWidth);
			int iEndY = Mathf.CeilToInt((iGridSubDivY + 1) * fGridSubDivPixelHeight);
			
			int iWidth = iEndX - iStartX;
			int iHeight = iEndY - iStartY;
			
			// Set grid sub div as empty while no significant pixel found
			bool bEmptyGridSubDiv = true;
			for( int iY = 0; bEmptyGridSubDiv && iY < iHeight; ++iY )
			{
				for( int iX = 0; bEmptyGridSubDiv && iX < iWidth; ++iX )
				{
					int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex( iX + iStartX, iY + iStartY );
					//int iPixelIndex = Mathf.FloorToInt( iGridSubDivY * fGridSubDivPixelHeight ) * iTextureWidth + Mathf.FloorToInt( iGridSubDivX * fGridSubDivPixelWidth ) + iY * iTextureWidth + iX;
					//bEmptyGridSubDiv = a_rBinarizedImage[ iPixelIndex ];

					// At least one pixel is significant => need to create the whole grid div
					if( a_rBinarizedImage[ iPixelIndex ] )
					{
						/*
						 * Grid div
						 * 
						 *	       ...       ...
						 *	        |         |
						 *	... --- TL ------- TR --- ...
						 *	        |   T2  / |
						 *	        |     /   |
						 *	        |   /     |
						 *	        | /   T1  |
						 *	... --- BL ------- BR --- ...
						 * 	        |         |
						 * 	       ...       ...
						 */
						Vector3 f3BottomLeftVertex  = new Vector3(         iGridSubDivX * fGridSubDivPixelWidth,         iGridSubDivY * fGridSubDivPixelHeight, 0.0f );	// BL
						Vector3 f3BottomRightVertex = new Vector3( ( iGridSubDivX + 1 ) * fGridSubDivPixelWidth,         iGridSubDivY * fGridSubDivPixelHeight, 0.0f );	// BR
						Vector3 f3TopRightVertex    = new Vector3( ( iGridSubDivX + 1 ) * fGridSubDivPixelWidth, ( iGridSubDivY + 1 ) * fGridSubDivPixelHeight, 0.0f );	// TR
						Vector3 f3TopLeftVertex     = new Vector3(         iGridSubDivX * fGridSubDivPixelWidth, ( iGridSubDivY + 1 ) * fGridSubDivPixelHeight, 0.0f );	// TL
						
						int iBottomLeftVertexIndex;
						int iBottomRightVertexIndex;
						int iTopRightVertexIndex;
						int iTopLeftVertexIndex;
						
						// For each grid div vertex, query its index if already created (the vertex might be shared with other grid divs).
						// If not, create it.
						if( oVertexPosIndexDict.TryGetValue( f3BottomLeftVertex, out iBottomLeftVertexIndex ) == false )
						{
							iBottomLeftVertexIndex = iCurrentVertexIndex++;
							oVertexPosIndexDict.Add( f3BottomLeftVertex, iBottomLeftVertexIndex );
							//oGridPlaneUVList.Add( new Vector2( f3BottomLeftVertex.x / (float) iTextureWidth, f3BottomLeftVertex.y / (float) iTextureHeight ) );
						}
						
						if( oVertexPosIndexDict.TryGetValue( f3BottomRightVertex, out iBottomRightVertexIndex ) == false )
						{
							iBottomRightVertexIndex = iCurrentVertexIndex++;
							oVertexPosIndexDict.Add( f3BottomRightVertex, iBottomRightVertexIndex );
							//oGridPlaneUVList.Add( new Vector2( f3BottomRightVertex.x / (float) iTextureWidth, f3BottomRightVertex.y / (float) iTextureHeight ) );
						}
						
						if( oVertexPosIndexDict.TryGetValue( f3TopRightVertex, out iTopRightVertexIndex ) == false )
						{
							iTopRightVertexIndex = iCurrentVertexIndex++;
							oVertexPosIndexDict.Add( f3TopRightVertex, iTopRightVertexIndex );
							//oGridPlaneUVList.Add( new Vector2( f3TopRightVertex.x / (float) iTextureWidth, f3TopRightVertex.y / (float) iTextureHeight ) );
						}
						
						if( oVertexPosIndexDict.TryGetValue( f3TopLeftVertex, out iTopLeftVertexIndex ) == false )
						{
							iTopLeftVertexIndex = iCurrentVertexIndex++;
							oVertexPosIndexDict.Add( f3TopLeftVertex, iTopLeftVertexIndex );
							//oGridPlaneUVList.Add( new Vector2( f3TopLeftVertex.x / (float) iTextureWidth, f3TopLeftVertex.y / (float) iTextureHeight ) );
						}
						
						// Create grid div triangles
						// First triangle (T1)
						oGridPlaneTriangleList.Add( iBottomRightVertexIndex );	// BR
						oGridPlaneTriangleList.Add( iBottomLeftVertexIndex );	// BL
						oGridPlaneTriangleList.Add( iTopRightVertexIndex );		// TR
						
						// Second triangle (T2)
						oGridPlaneTriangleList.Add( iBottomLeftVertexIndex );	// BL
						oGridPlaneTriangleList.Add( iTopLeftVertexIndex );		// TL
						oGridPlaneTriangleList.Add( iTopRightVertexIndex );		// TR

						// Set grid sub div as not empty
						bEmptyGridSubDiv = false;
					}
				}
			}
		}

		// Apply pivot + compute UVs
		int iVertexCount = oVertexPosIndexDict.Count;
		Vector2[ ] oGridPlaneUVs      = new Vector2[ iVertexCount ];
		Vector3[ ] oGridPlaneVertices = new Vector3[ iVertexCount ];
		Vector2 f2Dimensions          = new Vector2( 1.0f / (float) iTextureWidth, 1.0f / (float) iTextureHeight );

		foreach( KeyValuePair<Vector3,int> rVertexPosIndexPair in oVertexPosIndexDict )
		{
			Vector3 f3VertexPos = rVertexPosIndexPair.Key;
			int iIndex = rVertexPosIndexPair.Value;

			oGridPlaneUVs[ iIndex ]      = new Vector2( f3VertexPos.x * f2Dimensions.x, f3VertexPos.y * f2Dimensions.y );
			oGridPlaneVertices[ iIndex ] = ( f3VertexPos - a_f3PivotPoint ) * a_fScale;
		}
		
		oGridPlaneMesh.vertices  = oGridPlaneVertices;
		oGridPlaneMesh.uv        = oGridPlaneUVs;
		oGridPlaneMesh.triangles = oGridPlaneTriangleList.ToArray( );	// LINQ

		return oGridPlaneMesh;
	}
    public static void CombinedContourLabeling( BinarizedImage a_rBinarizedImage, bool a_bPolygonizeHoles, out List<Contour> a_rOuterContours, out List<Contour> a_rInnerContours )
    {
        a_rOuterContours = new List<Contour>( );
        a_rInnerContours = new List<Contour>( );

        int[ ] oLabelMap = new int[ a_rBinarizedImage.PixelCount ];	// init as 0
        int iRegionCounter = 0;

        for( int iY = 0; iY < a_rBinarizedImage.Height; ++iY )
        {
            int iLabel = 0;

            for( int iX = 0; iX < a_rBinarizedImage.Width; ++iX )
            {
                int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex( iX, iY );

                if( a_rBinarizedImage[ iPixelIndex ] == true )	// Foreground
                {
                    if( iLabel != 0 )	// Continue inside region
                    {
                        oLabelMap[ iPixelIndex ] = iLabel;
                    }
                    else
                    {
                        iLabel = oLabelMap[ iPixelIndex ];
                        if( iLabel == 0 )	// New outer contour
                        {
                            ++iRegionCounter;
                            iLabel = iRegionCounter;
                            Contour rOuterContour = TraceContour( iPixelIndex, NeighborDirection.DirectionRight, iLabel, a_rBinarizedImage, oLabelMap );
                            a_rOuterContours.Add( rOuterContour );
                            oLabelMap[ iPixelIndex ] = iLabel;
                        }
                    }
                }
                else // Background
                {
                    if( iLabel != 0 )
                    {
                        if( a_bPolygonizeHoles == true )
                        {
                            if( oLabelMap[ iPixelIndex ] == 0 )	// New inner contour
                            {
                                iPixelIndex = a_rBinarizedImage.Coords2PixelIndex( iX - 1, iY );
                                Contour rInnerContour = TraceContour( iPixelIndex, NeighborDirection.DirectionUpRight, iLabel, a_rBinarizedImage, oLabelMap );
                                a_rInnerContours.Add( rInnerContour );
                            }
                            iLabel = 0;
                        }
                        else if( oLabelMap[ iPixelIndex ] != -1 )
                        {
                            oLabelMap[ iPixelIndex ] = iLabel;
                        }
                        else
                        {
                            iLabel = 0;
                        }
                    }
                }
            }
        }
    }
	public static BinarizedImage DownScaleImage(BinarizedImage a_rBinarizedImage, int a_iHorizontalSubDivs, int a_iVerticalSubDivs)
	{
		int iImageWidth  = a_rBinarizedImage.Width;
		int iImageHeight = a_rBinarizedImage.Height;

		int iGridSubDivPerRow    = ( a_iVerticalSubDivs + 1 );
		int iGridSubDivPerColumn = ( a_iHorizontalSubDivs + 1 );
		int iGridSubDivCount     = iGridSubDivPerRow * iGridSubDivPerColumn;
		
		// Grid div dimensions
		float fGridSubDivPixelWidth  = ( (float) iImageWidth  ) / (float) iGridSubDivPerRow;
		float fGridSubDivPixelHeight = ( (float) iImageHeight ) / (float) iGridSubDivPerColumn;
		
		BinarizedImage oResizedBinarizedImage = new BinarizedImage( iGridSubDivPerRow, iGridSubDivPerColumn, false );
		// Iterate grid divs
		for( int iGridSubDivIndex = 0; iGridSubDivIndex < iGridSubDivCount; ++iGridSubDivIndex )
		{
			// ( X, Y ) grid div pos from grid div index (in grid div space)
			int iGridSubDivX = iGridSubDivIndex % iGridSubDivPerRow;
			int iGridSubDivY = iGridSubDivIndex / iGridSubDivPerRow;
			
			// Compute the pixel bounds for this subdivision
			int iStartX = Mathf.FloorToInt(iGridSubDivX * fGridSubDivPixelWidth);
			int iStartY = Mathf.FloorToInt(iGridSubDivY * fGridSubDivPixelHeight);
			
			int iEndX = Mathf.CeilToInt((iGridSubDivX + 1) * fGridSubDivPixelWidth);
			int iEndY = Mathf.CeilToInt((iGridSubDivY + 1) * fGridSubDivPixelHeight);
			
			int iWidth = iEndX - iStartX;
			int iHeight = iEndY - iStartY;
			
			// Set grid sub div as empty while no significant pixel found
			bool bEmptyGridSubDiv = true;
			float fGridSubDivAlphaMean = 0.0f;
			for( int iY = 0; iY < iHeight; ++iY )
			{
				for( int iX = 0; iX < iWidth; ++iX )
				{
					int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex( iX + iStartX, iY + iStartY );

					// At least one pixel is significant => need to create the whole grid div
					if( a_rBinarizedImage[ iPixelIndex ] )
					{
						bEmptyGridSubDiv = false;
					}
					fGridSubDivAlphaMean += a_rBinarizedImage.GetAlphaValue(iPixelIndex);
				}
			}
			fGridSubDivAlphaMean /= iHeight * iWidth;
			
			int iResizedIndex = oResizedBinarizedImage.Coords2PixelIndex( iGridSubDivX, iGridSubDivY );
			oResizedBinarizedImage.SetInternalPixel(iResizedIndex, !bEmptyGridSubDiv, fGridSubDivAlphaMean);
		}
		
		return oResizedBinarizedImage;
	}
    public static void CombinedContourLabeling(BinarizedImage a_rBinarizedImage, bool a_bPolygonizeHoles, out List <Contour> a_rOuterContours, out List <Contour> a_rInnerContours)
    {
        a_rOuterContours = new List <Contour>( );
        a_rInnerContours = new List <Contour>( );

        int[] oLabelMap      = new int[a_rBinarizedImage.PixelCount];           // init as 0
        int   iRegionCounter = 0;

        for (int iY = 0; iY < a_rBinarizedImage.Height; ++iY)
        {
            int iLabel = 0;

            for (int iX = 0; iX < a_rBinarizedImage.Width; ++iX)
            {
                int iPixelIndex = a_rBinarizedImage.Coords2PixelIndex(iX, iY);

                if (a_rBinarizedImage[iPixelIndex] == true)     // Foreground
                {
                    if (iLabel != 0)                            // Continue inside region
                    {
                        oLabelMap[iPixelIndex] = iLabel;
                    }
                    else
                    {
                        iLabel = oLabelMap[iPixelIndex];
                        if (iLabel == 0)                                // New outer contour
                        {
                            ++iRegionCounter;
                            iLabel = iRegionCounter;
                            Contour rOuterContour = TraceContour(iPixelIndex, NeighborDirection.DirectionRight, iLabel, a_rBinarizedImage, oLabelMap);
                            a_rOuterContours.Add(rOuterContour);
                            oLabelMap[iPixelIndex] = iLabel;
                        }
                    }
                }
                else                 // Background
                {
                    if (iLabel != 0)
                    {
                        if (a_bPolygonizeHoles == true)
                        {
                            if (oLabelMap[iPixelIndex] == 0)                                    // New inner contour
                            {
                                iPixelIndex = a_rBinarizedImage.Coords2PixelIndex(iX - 1, iY);
                                Contour rInnerContour = TraceContour(iPixelIndex, NeighborDirection.DirectionUpRight, iLabel, a_rBinarizedImage, oLabelMap);
                                a_rInnerContours.Add(rInnerContour);
                            }
                            iLabel = 0;
                        }
                        else if (oLabelMap[iPixelIndex] != -1)
                        {
                            oLabelMap[iPixelIndex] = iLabel;
                        }
                        else
                        {
                            iLabel = 0;
                        }
                    }
                }
            }
        }
    }