public static GridColliderPart LineGridColliderPart(GameObject parent, Grid grid, TileInfo tile, int x, int y, int w, int h)
        {
            GameObject obj = new GameObject("line");

            obj.transform.SetParent(parent.transform);

            obj.tag   = tile.tag;
            obj.layer = tile.layer;

            GridColliderPart part = obj.AddComponent <GridColliderPart> ();

            part.line = obj.AddComponent <EdgeCollider2D> ();
            part.line.usedByEffector = true;
            switch (tile.shape)
            {
            case TileShape.DOWN_ONEWAY: part.transform.Rotate(Vector3.forward * 180); break;

            case TileShape.LEFT_ONEWAY: part.transform.Rotate(Vector3.forward * 90); break;

            case TileShape.RIGHT_ONEWAY: part.transform.Rotate(-Vector3.forward * 90); break;
            }

            PlatformEffector2D effector = part.gameObject.AddComponent <PlatformEffector2D> ();

            effector.surfaceArc = 5;

            part.shape      = tile.shape;
            part.isVertical = tile.isVertical;

            part.id             = tile.id;
            part.line.isTrigger = tile.isTrigger;

            part.bottomLeftX = x;
            part.bottomLeftY = y;

            part.SetSize(w, h);

            return(part);
        }
        public bool Compatible(TileInfo info)
        {
            Collider2D collider = (box == null ? (triangle == null ? (line as Collider2D) : (triangle as Collider2D)) : (box as Collider2D));

            return(shape == info.shape && gameObject.tag == info.tag && gameObject.layer == info.layer && collider.isTrigger == info.isTrigger);
        }
        public static GridColliderPart TriangleGridColliderPart(GameObject parent, Grid grid, TileInfo tile, int x, int y, int w, int h)
        {
            GameObject obj = new GameObject("triangle");

            obj.transform.SetParent(parent.transform);

            obj.tag   = tile.tag;
            obj.layer = tile.layer;

            GridColliderPart part = obj.AddComponent <GridColliderPart> ();

            part.triangle = obj.AddComponent <PolygonCollider2D> ();

            part.shape      = tile.shape;
            part.isVertical = tile.isVertical;

            part.id = tile.id;
            part.triangle.isTrigger = tile.isTrigger;

            part.bottomLeftX = x;
            part.bottomLeftY = y;

            part.transform.localPosition = grid.TileSpaceToTransform(x + w / 2f, y + h / 2f);

            part.SetSize(w, h);

            return(part);
        }
        public static GridColliderPart CreateColliderPart(GameObject parent, Grid grid, TileInfo info, int x, int y, int w, int h)
        {
            switch (info.shape)
            {
            case TileShape.FULL:
                return(BoxGridColliderPart(parent, grid, info, x, y, w, h));

            case TileShape.DOWN_LEFT_TRIANGLE:
            case TileShape.DOWN_RIGHT_TRIANGLE:
            case TileShape.UP_LEFT_TRIANGLE:
            case TileShape.UP_RIGHT_TRIANGLE:
                return(TriangleGridColliderPart(parent, grid, info, x, y, w, h));

            case TileShape.DOWN_ONEWAY:
            case TileShape.UP_ONEWAY:
            case TileShape.LEFT_ONEWAY:
            case TileShape.RIGHT_ONEWAY:
                return(LineGridColliderPart(parent, grid, info, x, y, w, h));

            default: return(null);
            }
        }
        void JoinTriangle(PositionRegionRenderer currentRenderer, TileInfo info, Tile tile, int x, int y)
        {
            bool isExpanded = false;

            int relX = x - currentRenderer.regionX * Grid.REGION_SIZE;
            int relY = y - currentRenderer.regionY * Grid.REGION_SIZE;

            if (info.isVertical)
            {
                GridTriangle down = triangles.Get(x, y - 1) as GridTriangle;
                GridTriangle up   = triangles.Get(x, y + 1) as GridTriangle;

                if (down != null && down.id == tile.id && down.subId == tile.subId)
                {
                    down.height++;

                    triangles.Set(x, y, down);
                    isExpanded = true;
                }

                if (up != null && up.id == tile.id && up.subId == tile.subId)
                {
                    if (isExpanded)
                    {
                        down.height += up.height;

                        for (int i = y + 1; i <= y + up.height; i++)
                        {
                            triangles.Set(x, i, down);
                        }
                    }
                    else
                    {
                        up.height++;
                        up.bottomLeftY--;

                        triangles.Set(x, y, up);
                        isExpanded = true;
                    }
                }

                if (isExpanded)
                {
                    if (down != null)
                    {
                        int    actualSize;
                        Sprite sprite     = info.GetSprite(out actualSize, down.subId, down.height);
                        int    relBottomY = down.bottomLeftY - currentRenderer.regionY * Grid.REGION_SIZE;

                        if (relBottomY > Grid.REGION_SIZE / 2)
                        {
                            RenderUpTriangle(currentRenderer, relX, relBottomY, down.height, actualSize, sprite);
                        }
                        else
                        {
                            RenderDownTriangle(currentRenderer, relX, relBottomY, down.height, actualSize, sprite);
                        }
                    }
                    else if (up != null)
                    {
                        int    actualSize;
                        Sprite sprite     = info.GetSprite(out actualSize, up.subId, up.height);
                        int    relBottomY = up.bottomLeftY - currentRenderer.regionY * Grid.REGION_SIZE;

                        if (relBottomY > Grid.REGION_SIZE / 2)
                        {
                            RenderUpTriangle(currentRenderer, relX, relBottomY, up.height, actualSize, sprite);
                        }
                        else
                        {
                            RenderDownTriangle(currentRenderer, relX, relBottomY, up.height, actualSize, sprite);
                        }
                    }
                }
            }
            else
            {
                GridTriangle left  = triangles.Get(x - 1, y) as GridTriangle;
                GridTriangle right = triangles.Get(x + 1, y) as GridTriangle;

                if (left != null && left.id == tile.id && left.subId == tile.subId)
                {
                    left.width++;

                    triangles.Set(x, y, left);
                    isExpanded = true;
                }

                if (right != null && right.id == tile.id && right.subId == tile.subId)
                {
                    if (isExpanded)
                    {
                        left.width += right.width;

                        for (int i = x + 1; i <= x + right.width; i++)
                        {
                            triangles.Set(i, y, left);
                        }
                    }
                    else
                    {
                        right.width++;
                        right.bottomLeftX--;

                        triangles.Set(x, y, right);
                        isExpanded = true;
                    }
                }

                if (isExpanded)
                {
                    if (left != null)
                    {
                        int    actualSize;
                        Sprite sprite     = info.GetSprite(out actualSize, left.subId, left.width);
                        int    relBottomX = left.bottomLeftX - currentRenderer.regionX * Grid.REGION_SIZE;

                        if (relBottomX > Grid.REGION_SIZE / 2)
                        {
                            RenderRightTriangle(currentRenderer, relBottomX, relY, left.width, actualSize, sprite);
                        }
                        else
                        {
                            RenderLeftTriangle(currentRenderer, relBottomX, relY, left.width, actualSize, sprite);
                        }
                    }
                    else if (right != null)
                    {
                        int    actualSize;
                        Sprite sprite     = info.GetSprite(out actualSize, right.subId, right.width);
                        int    relBottomX = right.bottomLeftX - currentRenderer.regionX * Grid.REGION_SIZE;

                        if (relBottomX > Grid.REGION_SIZE / 2)
                        {
                            RenderRightTriangle(currentRenderer, relBottomX, relY, right.width, actualSize, sprite);
                        }
                        else
                        {
                            RenderLeftTriangle(currentRenderer, relBottomX, relY, right.width, actualSize, sprite);
                        }
                    }
                }
            }

            if (!isExpanded)
            {
                triangles.Set(x, y, GridTriangle.CreateTriangle(containerGO, grid, tile.id, tile.subId, info.isVertical, x, y, 1, 1));

                currentRenderer.mesh.SetTile(relX, relY, info.GetSprite(tile.subId));
            }
        }
        void Clear(GridTriangle triangle, PositionRegionRenderer currentRenderer, int x, int y)
        {
            int relX = x - currentRenderer.regionX * Grid.REGION_SIZE;
            int relY = y - currentRenderer.regionY * Grid.REGION_SIZE;

            if (triangle != null)
            {
                TileInfo info = grid.atlas [triangle.id];
                int      actualSize;

                if (triangle.isVertical)
                {
                    int bottomRelY = triangle.bottomLeftY - currentRenderer.regionY * Grid.REGION_SIZE;

                    if (y == triangle.bottomLeftY)
                    {
                        if (triangle.height == 1)
                        {
                            DestroyImmediate(triangle.gameObject);
                        }
                        else
                        {
                            triangle.height      -= 1;
                            triangle.bottomLeftY += 1;

                            Sprite sprite = info.GetSprite(out actualSize, triangle.subId, triangle.height);
                            RenderUpTriangle(currentRenderer, relX, bottomRelY + 1, triangle.height, actualSize, sprite);
                        }
                    }
                    else if (y == triangle.bottomLeftY + triangle.height - 1)
                    {
                        triangle.height -= 1;

                        Sprite sprite = info.GetSprite(out actualSize, triangle.subId, triangle.height);
                        RenderDownTriangle(currentRenderer, relX, bottomRelY, triangle.height, actualSize, sprite);
                    }
                    else
                    {
                        GridTriangle other = GridTriangle.CreateTriangle(
                            containerGO,
                            grid,
                            triangle.id, triangle.subId, true,
                            triangle.bottomLeftX, y + 1,
                            1, triangle.height - (y + 1 - triangle.bottomLeftY)
                            );

                        triangle.height = y - triangle.bottomLeftY;

                        Sprite sprite = info.GetSprite(out actualSize, triangle.subId, triangle.height);
                        RenderDownTriangle(currentRenderer, relX, bottomRelY, triangle.height, actualSize, sprite);

                        sprite     = info.GetSprite(out actualSize, other.subId, other.height);
                        bottomRelY = other.bottomLeftY - currentRenderer.regionY * Grid.REGION_SIZE;
                        for (int i = 0; i < other.height; i++)
                        {
                            triangles.Set(other.bottomLeftX, other.bottomLeftY + i, other);
                        }
                        RenderUpTriangle(currentRenderer, relX, bottomRelY, other.height, actualSize, sprite);
                    }
                }
                else
                {
                    int bottomRelX = triangle.bottomLeftX - currentRenderer.regionX * Grid.REGION_SIZE;

                    if (x == triangle.bottomLeftX)
                    {
                        if (triangle.width == 1)
                        {
                            DestroyImmediate(triangle.gameObject);
                        }
                        else
                        {
                            triangle.width       -= 1;
                            triangle.bottomLeftX += 1;

                            Sprite sprite = info.GetSprite(out actualSize, triangle.subId, triangle.width);
                            RenderRightTriangle(currentRenderer, bottomRelX + 1, relY, triangle.width, actualSize, sprite);
                        }
                    }
                    else if (x == triangle.bottomLeftX + triangle.width - 1)
                    {
                        triangle.width -= 1;

                        Sprite sprite = info.GetSprite(out actualSize, triangle.subId, triangle.width);
                        RenderLeftTriangle(currentRenderer, bottomRelX, relY, triangle.width, actualSize, sprite);
                    }
                    else
                    {
                        GridTriangle other = GridTriangle.CreateTriangle(
                            containerGO,
                            grid,
                            triangle.id, triangle.subId, false,
                            x + 1, triangle.bottomLeftY,
                            triangle.width - (x + 1 - triangle.bottomLeftX), 1
                            );

                        triangle.width = x - triangle.bottomLeftX;

                        Sprite sprite = info.GetSprite(out actualSize, triangle.subId, triangle.width);
                        RenderLeftTriangle(currentRenderer, bottomRelX, relY, triangle.width, actualSize, sprite);

                        sprite     = info.GetSprite(out actualSize, other.subId, other.width);
                        bottomRelX = other.bottomLeftX - currentRenderer.regionX * Grid.REGION_SIZE;
                        for (int i = 0; i < other.width; i++)
                        {
                            triangles.Set(other.bottomLeftX + i, other.bottomLeftY, other);
                        }
                        RenderRightTriangle(currentRenderer, bottomRelX, relY, other.width, actualSize, sprite);
                    }
                }

                triangles.Set(x, y, null);
            }

            currentRenderer.mesh.Clear(relX, relY);
        }
        public override void OnSet(int x, int y, Tile tile)
        {
            // clear previous
            ClearTile(x, y);

            TileInfo info = grid.atlas [tile.id];

            // add new
            if (info.shape != TileShape.EMPTY)
            {
                GridColliderPart wrapper = null;

                // HORIZONTAL GROWTH
                if (info.shape != TileShape.LEFT_ONEWAY && info.shape != TileShape.RIGHT_ONEWAY)
                {
                    bool expanded = false;

                    GridColliderPart left = components.Get(x - 1, y) as GridColliderPart;
                    if (left != null && left.Compatible(info) && !left.isVertical && !info.isVertical)
                    {
                        left.SetSize(left.width + 1, 1);
                        wrapper = left;

                        components.Set(x, y, left);

                        expanded = true;
                    }

                    GridColliderPart right = components.Get(x + 1, y) as GridColliderPart;
                    if (right != null && right.Compatible(info) && !right.isVertical && !info.isVertical)
                    {
                        if (!expanded)
                        {
                            right.bottomLeftX -= 1;
                            right.SetSize(right.width + 1, 1);
                            wrapper = right;

                            components.Set(x, y, right);

                            wrapper.ResetSizeAndPosition(grid);
                            return;
                        }
                        else
                        {
                            left.SetSize(left.width + right.width, 1);

                            for (int i = right.bottomLeftX; i < right.bottomLeftX + right.width; i++)
                            {
                                components.Set(i, y, left);
                            }

                            if (Application.isPlaying)
                            {
                                Destroy(right.gameObject);
                            }
                            else
                            {
                                DestroyImmediate(right.gameObject);
                            }

                            wrapper.ResetSizeAndPosition(grid);
                            return;
                        }
                    }

                    if (expanded)
                    {
                        wrapper.ResetSizeAndPosition(grid);
                        return;
                    }
                }

                // VERTICAL GROWTH
                if (info.shape != TileShape.FULL && info.shape != TileShape.UP_ONEWAY && info.shape != TileShape.DOWN_ONEWAY)
                {
                    bool expanded = false;

                    GridColliderPart down = components.Get(x, y - 1) as GridColliderPart;
                    if (down != null && down.Compatible(info) && down.isVertical && info.isVertical)
                    {
                        down.SetSize(1, down.height + 1);
                        wrapper = down;

                        components.Set(x, y, down);

                        expanded = true;
                    }

                    GridColliderPart up = components.Get(x, y + 1) as GridColliderPart;

                    if (up != null && up.Compatible(info) && up.isVertical && info.isVertical)
                    {
                        if (!expanded)
                        {
                            up.bottomLeftY -= 1;
                            up.SetSize(1, up.height + 1);
                            wrapper = up;

                            components.Set(x, y, up);

                            wrapper.ResetSizeAndPosition(grid);
                            return;
                        }
                        else
                        {
                            down.SetSize(1, down.height + up.height);

                            for (int i = up.bottomLeftY; i < up.bottomLeftY + up.width; i++)
                            {
                                components.Set(i, y, down);
                            }

                            if (Application.isPlaying)
                            {
                                Destroy(up.gameObject);
                            }
                            else
                            {
                                DestroyImmediate(up.gameObject);
                            }

                            wrapper.ResetSizeAndPosition(grid);
                            return;
                        }
                    }

                    if (expanded)
                    {
                        wrapper.ResetSizeAndPosition(grid);
                        return;
                    }
                }

                // NO EXPANSE, CREATE NEW
                wrapper = GridColliderPart.CreateColliderPart(containerGO, grid, info, x, y, 1, 1);
                components.Set(x, y, wrapper);

                wrapper.ResetSizeAndPosition(grid);
            }
        }
        public override void OnShowRegion(int regionX, int regionY)
        {
            int bx = regionX * Grid.REGION_SIZE;
            int by = regionY * Grid.REGION_SIZE;

            FiniteGrid          region           = grid.GetRegion(regionX, regionY);
            FiniteComponentGrid regionComponents = components.GetOrCreateRegion(regionX, regionY);

            for (int y = 0; y < Grid.REGION_SIZE; y++)
            {
                GridColliderPart currentWrapper = components.Get(bx - 1, y + by) as GridColliderPart;

                for (int x = 0; x < Grid.REGION_SIZE - 1; x++)
                {
                    Tile tile = region.Get(x, y);

                    if (tile != null)
                    {
                        TileInfo info = grid.atlas [tile.id];

                        if (currentWrapper != null && currentWrapper.Compatible(info) && !info.isVertical && !currentWrapper.isVertical)
                        {
                            currentWrapper.width++;
                        }
                        else
                        {
                            if (currentWrapper != null)
                            {
                                currentWrapper.ResetSizeAndPosition(grid);
                            }

                            if (info.shape != TileShape.EMPTY && !info.isVertical)
                            {
                                currentWrapper = GridColliderPart.CreateColliderPart(containerGO, grid, info, bx + x, by + y, 1, 1);
                            }
                            else
                            {
                                currentWrapper = null;
                            }
                        }

                        regionComponents.Set(x, y, currentWrapper);
                    }
                    else
                    {
                        if (currentWrapper != null)
                        {
                            currentWrapper.ResetSizeAndPosition(grid);
                            currentWrapper = null;
                        }

                        regionComponents.Set(x, y, null);
                    }
                }

                if (currentWrapper != null)
                {
                    currentWrapper.ResetSizeAndPosition(grid);
                }

                Tile edgeTile = region.Get(Grid.REGION_SIZE - 1, y);
                if (edgeTile != null)
                {
                    OnSet(bx + Grid.REGION_SIZE - 1, by + y, edgeTile);
                }
            }

            for (int x = 0; x < Grid.REGION_SIZE; x++)
            {
                GridColliderPart currentWrapper = components.Get(bx + x, by - 1) as GridColliderPart;

                for (int y = 0; y < Grid.REGION_SIZE - 1; y++)
                {
                    Tile tile = region.Get(x, y);

                    if (tile != null)
                    {
                        TileInfo info = grid.atlas [tile.id];

                        if (currentWrapper != null && currentWrapper.Compatible(info) && info.isVertical && currentWrapper.isVertical)
                        {
                            currentWrapper.height++;

                            regionComponents.Set(x, y, currentWrapper);
                        }
                        else
                        {
                            if (currentWrapper != null)
                            {
                                currentWrapper.ResetSizeAndPosition(grid);
                            }

                            if (info.shape != TileShape.EMPTY && info.isVertical)
                            {
                                currentWrapper = GridColliderPart.CreateColliderPart(containerGO, grid, info, bx + x, by + y, 1, 1);

                                regionComponents.Set(x, y, currentWrapper);
                            }
                            else
                            {
                                currentWrapper = null;
                            }
                        }
                    }
                    else
                    {
                        if (currentWrapper != null)
                        {
                            currentWrapper.ResetSizeAndPosition(grid);
                            currentWrapper = null;
                        }
                    }
                }

                if (currentWrapper != null)
                {
                    currentWrapper.ResetSizeAndPosition(grid);
                }

                Tile edgeTile = region.Get(x, Grid.REGION_SIZE - 1);
                if (edgeTile != null && grid.atlas[edgeTile.id].isVertical)
                {
                    OnSet(bx + x, by + Grid.REGION_SIZE - 1, edgeTile);
                }
            }
        }