Example #1
0
        private void CreateJigsawPuzzle()
        {
            int pieceWidth  = _sourcePicture.Width / GameSettings.NUM_COLUMNS;
            int pieceHeight = _sourcePicture.Height / GameSettings.NUM_ROWS;


            int lastColPieceWidth  = pieceWidth + (_sourcePicture.Width % GameSettings.NUM_COLUMNS);
            int lastRowPieceHeight = pieceHeight + (_sourcePicture.Height % GameSettings.NUM_ROWS);



            int lastRow = (GameSettings.NUM_ROWS - 1);
            int lastCol = (GameSettings.NUM_COLUMNS - 1);

            _currentCluster = null;
            _clusters       = new List <PieceCluster>();

            Matrix matrix = new Matrix();

            Pen outlinePen = new Pen(Color.Black)
            {
                Width     = GameSettings.PIECE_OUTLINE_WIDTH,
                Alignment = PenAlignment.Inset
            };

            int pieceID = 0;

            for (int row = 0; row < GameSettings.NUM_ROWS; row++)
            {
                bool topCurveFlipVertical    = (row % 2 == 0);
                bool bottomCurveFlipVertical = (row % 2 != 0);

                for (int col = 0; col < GameSettings.NUM_COLUMNS; col++)
                {
                    bool leftCurveFlipHorizontal  = (col % 2 != 0);
                    bool rightCurveFlipHorizontal = (col % 2 == 0);


                    if (row % 2 == 0)
                    {
                        leftCurveFlipHorizontal  = (col % 2 == 0);
                        rightCurveFlipHorizontal = (col % 2 != 0);
                    }


                    topCurveFlipVertical    = !topCurveFlipVertical;
                    bottomCurveFlipVertical = !bottomCurveFlipVertical;

                    GraphicsPath figure = new GraphicsPath();

                    int offsetX = (col * pieceWidth);
                    int offsetY = (row * pieceHeight);
                    int horizontalCurveLength = (col == lastCol ? lastColPieceWidth : pieceWidth);
                    int verticalCurveLength   = (row == lastRow ? lastRowPieceHeight : pieceHeight);

                    // Top

                    if (row == 0)
                    {
                        int startX = offsetX;
                        int startY = offsetY;
                        int endX   = offsetX + horizontalCurveLength;
                        int endY   = offsetY;

                        figure.AddLine(startX, startY, endX, endY);
                    }
                    else
                    {
                        BezierCurve topCurve = BezierCurve.CreateHorizontal(horizontalCurveLength);

                        if (topCurveFlipVertical)
                        {
                            topCurve.FlipVertical();
                        }

                        topCurve.Translate(offsetX, offsetY);
                        figure.AddBeziers(topCurve.Points);
                    }



                    //    Right

                    if (col == lastCol)
                    {
                        int startX = offsetX + lastColPieceWidth;
                        int startY = offsetY;
                        int endX   = offsetX + lastColPieceWidth;
                        int endY   = offsetY + verticalCurveLength;

                        figure.AddLine(startX, startY, endX, endY);
                    }
                    else
                    {
                        BezierCurve verticalCurve = BezierCurve.CreateVertical(verticalCurveLength);

                        if (rightCurveFlipHorizontal)
                        {
                            verticalCurve.FlipHorizontal();
                        }

                        verticalCurve.Translate(offsetX + pieceWidth, offsetY);
                        figure.AddBeziers(verticalCurve.Points);
                    }



                    //    Bottom

                    if (row == lastRow)
                    {
                        int startX = offsetX;
                        int startY = offsetY + lastRowPieceHeight;
                        int endX   = offsetX + horizontalCurveLength;
                        int endY   = offsetY + lastRowPieceHeight;

                        figure.AddLine(endX, endY, startX, startY);
                    }
                    else
                    {
                        BezierCurve bottomCurve = BezierCurve.CreateHorizontal(horizontalCurveLength);
                        bottomCurve.FlipHorizontal();

                        if (bottomCurveFlipVertical)
                        {
                            bottomCurve.FlipVertical();
                        }

                        bottomCurve.Translate(offsetX + horizontalCurveLength, offsetY + pieceHeight);
                        figure.AddBeziers(bottomCurve.Points);
                    }



                    //     Left

                    if (col == 0)
                    {
                        int startX = offsetX;
                        int startY = offsetY;
                        int endX   = offsetX;
                        int endY   = offsetY + verticalCurveLength;

                        figure.AddLine(endX, endY, startX, startY);
                    }
                    else
                    {
                        BezierCurve verticalCurve = BezierCurve.CreateVertical(verticalCurveLength);
                        verticalCurve.FlipVertical();

                        if (leftCurveFlipHorizontal)
                        {
                            verticalCurve.FlipHorizontal();
                        }

                        verticalCurve.Translate(offsetX, offsetY + verticalCurveLength);
                        figure.AddBeziers(verticalCurve.Points);
                    }



                    List <Coordinate> adjacentCoords = new List <Coordinate>
                    {
                        new Coordinate(col, row - 1),
                        new Coordinate(col + 1, row),
                        new Coordinate(col, row + 1),
                        new Coordinate(col - 1, row)
                    };

                    List <int> adjacentPieceIDs = DetermineAdjacentPieceIDs(adjacentCoords, GameSettings.NUM_COLUMNS);



                    //   Постр картинку

                    Rectangle figureLocation = Rectangle.Truncate(figure.GetBounds());


                    matrix.Reset();
                    matrix.Translate(0 - figureLocation.X, 0 - figureLocation.Y);
                    GraphicsPath translatedFigure = (GraphicsPath)figure.Clone();
                    translatedFigure.Transform(matrix);

                    Rectangle translatedFigureLocation = Rectangle.Truncate(translatedFigure.GetBounds());

                    Bitmap piecePicture = new Bitmap(figureLocation.Width, figureLocation.Height);

                    using (Graphics gfx = Graphics.FromImage(piecePicture))
                    {
                        gfx.FillRectangle(Brushes.White, 0, 0, piecePicture.Width, piecePicture.Height);
                        gfx.ResetClip();
                        gfx.SetClip(translatedFigure);
                        gfx.DrawImage(_sourcePicture, new Rectangle(0, 0, piecePicture.Width, piecePicture.Height),
                                      figureLocation, GraphicsUnit.Pixel);

                        if (GameSettings.DRAW_PIECE_OUTLINE)
                        {
                            gfx.SmoothingMode = SmoothingMode.AntiAlias;
                            gfx.DrawPath(outlinePen, translatedFigure);
                        }
                    }


                    Bitmap modifiedPiecePicture = (Bitmap)piecePicture.Clone();
                    ImageUtilities.EdgeDetectHorizontal(modifiedPiecePicture);
                    ImageUtilities.EdgeDetectVertical(modifiedPiecePicture);
                    piecePicture = ImageUtilities.AlphaBlendMatrix(modifiedPiecePicture, piecePicture, 200);



                    Piece piece = new Piece
                    {
                        ID                    = pieceID,
                        ClusterID             = pieceID,
                        Width                 = figureLocation.Width,
                        Height                = figureLocation.Height,
                        BoardLocation         = translatedFigureLocation,
                        SourcePictureLocation = figureLocation,
                        MovableFigure         = (GraphicsPath)translatedFigure.Clone(),
                        StaticFigure          = (GraphicsPath)figure.Clone(),
                        Picture               = (Bitmap)piecePicture.Clone(),
                        AdjacentPieceIDs      = adjacentPieceIDs
                    };

                    PieceCluster cluster = new PieceCluster
                    {
                        ID                    = pieceID,
                        Width                 = figureLocation.Width,
                        Height                = figureLocation.Height,
                        BoardLocation         = translatedFigureLocation,
                        SourcePictureLocation = figureLocation,
                        MovableFigure         = (GraphicsPath)translatedFigure.Clone(),
                        StaticFigure          = (GraphicsPath)figure.Clone(),
                        Picture               = (Bitmap)piecePicture.Clone(),
                        Pieces                = new List <Piece> {
                            piece
                        }
                    };



                    _clusters.Add(cluster);



                    pieceID++;
                }
            }



            Random random = new Random();

            int boardWidth  = this.ClientSize.Width;
            int boardHeight = this.ClientSize.Height;

            foreach (PieceCluster cluster in _clusters)
            {
                int locationX = random.Next(1, boardWidth);
                int locationY = random.Next((menuStrip1.Height + 1), boardHeight);


                if ((locationX + cluster.Width) > boardWidth)
                {
                    locationX = locationX - ((locationX + cluster.Width) - boardWidth);
                }

                if ((locationY + cluster.Height) > boardHeight)
                {
                    locationY = locationY - ((locationY + cluster.Height) - boardHeight);
                }



                for (int index = 0; index < cluster.Pieces.Count; index++)
                {
                    Piece piece = cluster.Pieces[index];
                    piece.BoardLocation = new Rectangle(locationX, locationY, piece.Width, piece.Height);

                    matrix.Reset();
                    matrix.Translate(locationX, locationY);
                    piece.MovableFigure.Transform(matrix);
                }


                cluster.BoardLocation = new Rectangle(locationX, locationY, cluster.Width, cluster.Height);

                matrix.Reset();
                matrix.Translate(locationX, locationY);
                cluster.MovableFigure.Transform(matrix);
            }
        }
Example #2
0
        private void MainForm_MouseUp(object sender, MouseEventArgs e)
        {
            if (_canMovePiece)
            {
                _previousMouseX = e.X;
                _previousMouseY = e.Y;

                using (Graphics gfx = Graphics.FromImage(_backBuffer))
                {
                    gfx.ResetClip();
                    gfx.SetClip(_currentCluster.MovableFigure);
                    gfx.DrawImageUnscaled(_currentCluster.Picture, _currentCluster.BoardLocation);
                }

                using (Graphics gfx = Graphics.FromImage(_board))
                {
                    gfx.DrawImageUnscaled(_backBuffer, 0, 0);
                }

                using (Graphics gfx = this.CreateGraphics())
                {
                    gfx.DrawImageUnscaled(_backBuffer, 0, 0);
                }


                // Соед части маз

                Matrix matrix = new Matrix();

                // соед смежных частией в один кластер
                List <int> adjacentClusterIDs = new List <int>();

                // проверка если ли рядом нужный кусок
                for (int i = 0; i < _currentCluster.Pieces.Count; i++)
                {
                    Piece currentPiece = _currentCluster.Pieces[i];

                    foreach (int pieceID in currentPiece.AdjacentPieceIDs)
                    {
                        Piece adjacentPiece = GetPieceByID(pieceID);

                        if (adjacentPiece != null && (adjacentPiece.ClusterID != currentPiece.ClusterID))
                        {
                            // соед частей если нужный кусок находится справо

                            Rectangle adjacentPieceMovableFigureBoardLocation = Rectangle.Truncate(adjacentPiece.MovableFigure.GetBounds());
                            Rectangle currentPieceMovableFigureBoardLocation  = Rectangle.Truncate(currentPiece.MovableFigure.GetBounds());


                            // разбежка 2 пикселя соеденит две части
                            if (Math.Abs(currentPiece.SourcePictureLocation.X - adjacentPiece.SourcePictureLocation.X) <= 2)
                            {
                                int figureYDifferenceSign        = Math.Sign(currentPieceMovableFigureBoardLocation.Y - adjacentPieceMovableFigureBoardLocation.Y);
                                int sourcePictureYDifferenceSign = Math.Sign(currentPiece.SourcePictureLocation.Y - adjacentPiece.SourcePictureLocation.Y);

                                // если нужный кусок находится не с той стороный которой нужно то не соед
                                if (figureYDifferenceSign != sourcePictureYDifferenceSign)
                                {
                                    continue;
                                }
                            }
                            else if (Math.Abs(currentPiece.SourcePictureLocation.Y - adjacentPiece.SourcePictureLocation.Y) <= 2)
                            {
                                int figureXDifferenceSign      = Math.Sign(currentPieceMovableFigureBoardLocation.X - adjacentPieceMovableFigureBoardLocation.X);
                                int sourceImageXDifferenceSign = Math.Sign(currentPiece.SourcePictureLocation.X - adjacentPiece.SourcePictureLocation.X);

                                if (figureXDifferenceSign != sourceImageXDifferenceSign)
                                {
                                    continue;
                                }
                            }

                            GraphicsPath combinedMovableFigure = new GraphicsPath();
                            combinedMovableFigure.AddPath(adjacentPiece.MovableFigure, false);
                            combinedMovableFigure.AddPath(currentPiece.MovableFigure, false);

                            Rectangle combinedMovableFigureBoardLocation = Rectangle.Truncate(combinedMovableFigure.GetBounds());

                            Rectangle combinedSourcePictureLocation = Rectangle.Union(adjacentPiece.SourcePictureLocation, currentPiece.SourcePictureLocation);

                            if (Math.Abs(combinedMovableFigureBoardLocation.Width - combinedSourcePictureLocation.Width) <= GameSettings.SNAP_TOLERANCE &&
                                Math.Abs(combinedMovableFigureBoardLocation.Height - combinedSourcePictureLocation.Height) <= GameSettings.SNAP_TOLERANCE)
                            {
                                PieceCluster adjacentPieceCluster = GetPieceClusterByID(adjacentPiece.ClusterID);

                                adjacentClusterIDs.Add(adjacentPieceCluster.ID);

                                // обновление кластера для соед частей
                                foreach (Piece piece in adjacentPieceCluster.Pieces)
                                {
                                    piece.ClusterID = currentPiece.ClusterID;
                                }
                            }
                        }
                    }
                }

                if (adjacentClusterIDs.Count > 0)
                {
                    foreach (int clusterID in adjacentClusterIDs)
                    {
                        PieceCluster adjacentCluster = GetPieceClusterByID(clusterID);

                        foreach (Piece piece in adjacentCluster.Pieces)
                        {
                            _currentCluster.Pieces.Add(piece);
                        }

                        RemovePieceGroupByID(clusterID);
                    }

                    GraphicsPath combinedStaticFigure          = new GraphicsPath();
                    Rectangle    combinedBoardLocation         = _currentCluster.BoardLocation;
                    Rectangle    combinedSourcePictureLocation = _currentCluster.SourcePictureLocation;

                    foreach (Piece piece in _currentCluster.Pieces)
                    {
                        combinedStaticFigure.AddPath(piece.StaticFigure, false);
                        combinedBoardLocation         = Rectangle.Union(combinedBoardLocation, piece.BoardLocation);
                        combinedSourcePictureLocation = Rectangle.Union(combinedSourcePictureLocation, piece.SourcePictureLocation);
                    }

                    _currentCluster.BoardLocation = new Rectangle(combinedBoardLocation.X, combinedBoardLocation.Y,
                                                                  combinedSourcePictureLocation.Width,
                                                                  combinedSourcePictureLocation.Height);

                    _currentCluster.SourcePictureLocation = combinedSourcePictureLocation;
                    _currentCluster.Width         = combinedSourcePictureLocation.Width;
                    _currentCluster.Height        = combinedSourcePictureLocation.Height;
                    _currentCluster.StaticFigure  = (GraphicsPath)combinedStaticFigure.Clone();
                    _currentCluster.MovableFigure = (GraphicsPath)combinedStaticFigure.Clone();

                    Rectangle combinedStaticFigureLocation = Rectangle.Truncate(combinedStaticFigure.GetBounds());

                    matrix.Reset();
                    matrix.Translate(0 - combinedStaticFigureLocation.X, 0 - combinedStaticFigureLocation.Y);
                    _currentCluster.MovableFigure.Transform(matrix);

                    matrix.Reset();
                    matrix.Translate(combinedBoardLocation.X, combinedBoardLocation.Y);
                    _currentCluster.MovableFigure.Transform(matrix);

                    // строим картику

                    matrix.Reset();
                    matrix.Translate(0 - combinedStaticFigureLocation.X, 0 - combinedStaticFigureLocation.Y);
                    GraphicsPath translatedCombinedStaticFigure = (GraphicsPath)combinedStaticFigure.Clone();
                    translatedCombinedStaticFigure.Transform(matrix);

                    Bitmap clusterPicture = new Bitmap(combinedSourcePictureLocation.Width, combinedSourcePictureLocation.Height);

                    using (Graphics gfx = Graphics.FromImage(clusterPicture))
                    {
                        gfx.FillRectangle(Brushes.White, 0, 0, clusterPicture.Width, clusterPicture.Height);

                        gfx.ResetClip();
                        gfx.SetClip(translatedCombinedStaticFigure);
                        gfx.DrawImage(_sourcePicture, new Rectangle(0, 0, clusterPicture.Width, clusterPicture.Height),
                                      combinedStaticFigureLocation, GraphicsUnit.Pixel);

                        if (GameSettings.DRAW_PIECE_OUTLINE)
                        {
                            Pen outlinePen = new Pen(Color.Black)
                            {
                                Width     = GameSettings.PIECE_OUTLINE_WIDTH,
                                Alignment = PenAlignment.Inset
                            };

                            gfx.SmoothingMode = SmoothingMode.AntiAlias;
                            gfx.DrawPath(outlinePen, translatedCombinedStaticFigure);
                        }
                    }

                    Bitmap modifiedClusterPicture = (Bitmap)clusterPicture.Clone();
                    ImageUtilities.EdgeDetectHorizontal(modifiedClusterPicture);
                    ImageUtilities.EdgeDetectVertical(modifiedClusterPicture);
                    clusterPicture = ImageUtilities.AlphaBlendMatrix(modifiedClusterPicture, clusterPicture, 200);



                    _currentCluster.Picture = (Bitmap)clusterPicture.Clone();


                    foreach (Piece piece in _currentCluster.Pieces)
                    {
                        int offsetX = piece.SourcePictureLocation.X - combinedSourcePictureLocation.X;
                        int offsetY = piece.SourcePictureLocation.Y - combinedSourcePictureLocation.Y;

                        int newLocationX = combinedBoardLocation.X + offsetX;
                        int newLocationY = combinedBoardLocation.Y + offsetY;

                        piece.BoardLocation = new Rectangle(newLocationX, newLocationY, piece.Width, piece.Height);

                        Rectangle movableFigureBoardLocation = Rectangle.Truncate(piece.MovableFigure.GetBounds());

                        matrix.Reset();
                        matrix.Translate(0 - movableFigureBoardLocation.X, 0 - movableFigureBoardLocation.Y);
                        piece.MovableFigure.Transform(matrix);

                        matrix.Reset();
                        matrix.Translate(newLocationX, newLocationY);
                        piece.MovableFigure.Transform(matrix);
                    }


                    // перерисовка обновление всего

                    Rectangle areaToClear = new Rectangle(combinedBoardLocation.X, combinedBoardLocation.Y,
                                                          combinedBoardLocation.Width + GameSettings.DROP_SHADOW_DEPTH,
                                                          combinedBoardLocation.Height + GameSettings.DROP_SHADOW_DEPTH);

                    using (Graphics gfx = Graphics.FromImage(_backBuffer))
                    {
                        // очистка фона
                        gfx.DrawImage(_background, areaToClear, areaToClear, GraphicsUnit.Pixel);



                        Region regionToRedraw = new Region(areaToClear);

                        foreach (PieceCluster cluster in _clusters)
                        {
                            Region clusterRegion = new Region(cluster.MovableFigure);
                            clusterRegion.Intersect(regionToRedraw);

                            if (!clusterRegion.IsEmpty(gfx))
                            {
                                gfx.SetClip(clusterRegion, CombineMode.Replace);
                                gfx.DrawImageUnscaled(cluster.Picture, cluster.BoardLocation);
                            }
                        }
                    }



                    using (Graphics gfx = Graphics.FromImage(_board))
                    {
                        gfx.DrawImageUnscaled(_backBuffer, 0, 0);
                    }



                    using (Graphics gfx = this.CreateGraphics())
                    {
                        gfx.DrawImageUnscaled(_backBuffer, 0, 0);
                    }
                }



                _canMovePiece   = false;
                _currentCluster = null;

                // победка

                if (_clusters.Count == 1)
                {
                    if (_victoryAnnounced == false)
                    {
                        _victoryAnnounced = true;
                        MessageBox.Show("Победа!!!", "Победа!!!", MessageBoxButtons.OK);
                    }
                }
            }
        }
Example #3
0
        private ResponseMessage DisplayJigsawPuzzle(bool showGhostPicture)
        {
            if (_sourcePicture == null)
            {
                return(new ResponseMessage
                {
                    Message = "Please provide source picture."
                });
            }

            int boardWidth  = this.ClientSize.Width;
            int boardHeight = this.ClientSize.Height;

            if (boardHeight < 100 || boardWidth < 100)
            {
                return(new ResponseMessage
                {
                    Message = "No source picture loaded."
                });
            }
            _board      = new Bitmap(boardWidth, boardHeight);
            _backBuffer = new Bitmap(boardWidth, boardHeight);
            _background = new Bitmap(boardWidth, boardHeight);

            #region Background tile image

            using (Graphics gfx = Graphics.FromImage(_background))
            {
                if (File.Exists(GameSettings.BACKGROUND_PICTURE_NAME))
                {
                    // This background image is ripped from "Jigsaw Puzzle Golden Edition"
                    Bitmap       tileImage = new Bitmap("background_tile.bmp");
                    TextureBrush tileBrush = new TextureBrush(tileImage);

                    gfx.FillRectangle(tileBrush, 0, 0, _background.Width, _background.Height);
                }
                else
                {
                    SolidBrush colorBrush = new SolidBrush(Color.FromArgb(0, 138, 184));

                    gfx.FillRectangle(colorBrush, 0, 0, _background.Width, _background.Height);
                }
            }

            if (showGhostPicture)
            {
                _background = ImageUtilities.AlphaBlendMatrix(_background, _sourcePicture, GameSettings.GHOST_PICTURE_ALPHA);
            }

            #endregion

            #region Board, backbuffer, and the form

            using (Graphics gfx = Graphics.FromImage(_board))
            {
                gfx.DrawImageUnscaled(_background, 0, 0);

                foreach (PieceCluster cluster in _clusters)
                {
                    gfx.ResetClip();
                    gfx.SetClip(cluster.MovableFigure);
                    gfx.DrawImage(cluster.Picture, cluster.BoardLocation);
                }
            }

            using (Graphics gfx = Graphics.FromImage(_backBuffer))
            {
                gfx.DrawImageUnscaled(_board, 0, 0);
            }

            using (Graphics gfx = this.CreateGraphics())
            {
                gfx.DrawImageUnscaled(_board, 0, 0);
            }

            #endregion

            return(new ResponseMessage
            {
                Okay = true
            });
        }
Example #4
0
        private void MainForm_MouseUp(object sender, MouseEventArgs e)
        {
            if (_canMovePiece)
            {
                _previousMouseX = e.X;
                _previousMouseY = e.Y;

                #region Draw the "dropped" moving cluster at its final position into back buffer

                using (Graphics gfx = Graphics.FromImage(_backBuffer))
                {
                    gfx.ResetClip();
                    gfx.SetClip(_currentCluster.MovableFigure);
                    gfx.DrawImageUnscaled(_currentCluster.Picture, _currentCluster.BoardLocation);
                }

                #endregion

                #region Sync the board, the back buffer, and the display

                using (Graphics gfx = Graphics.FromImage(_board))
                {
                    gfx.DrawImageUnscaled(_backBuffer, 0, 0);
                }

                using (Graphics gfx = this.CreateGraphics())
                {
                    gfx.DrawImageUnscaled(_backBuffer, 0, 0);
                }

                #endregion

                #region Snapping and combining adjacent pieces

                Matrix matrix = new Matrix();

                // Adjacent clusters to be combined with the current cluster
                List <int> adjacentClusterIDs = new List <int>();

                // For each piece in the current cluster, check if there is any adjacent piece to snap
                for (int i = 0; i < _currentCluster.Pieces.Count; i++)
                {
                    Piece currentPiece = _currentCluster.Pieces[i];

                    foreach (int pieceID in currentPiece.AdjacentPieceIDs)
                    {
                        Piece adjacentPiece = GetPieceByID(pieceID);

                        if (adjacentPiece != null && (adjacentPiece.ClusterID != currentPiece.ClusterID))
                        {
                            #region Make sure the adjacent piece is located at the correct "side" of the current piece

                            Rectangle adjacentPieceMovableFigureBoardLocation = Rectangle.Truncate(adjacentPiece.MovableFigure.GetBounds());
                            Rectangle currentPieceMovableFigureBoardLocation  = Rectangle.Truncate(currentPiece.MovableFigure.GetBounds());

                            // Math.Sign(.) returns a number indicating the sign of value.
                            // -1 value is less than zero. 0 value is equal to zero. 1 value is greater than zero.
                            // Tolerance value is set to 2 pixels.
                            if (Math.Abs(currentPiece.SourcePictureLocation.X - adjacentPiece.SourcePictureLocation.X) <= 2)
                            {
                                int figureYDifferenceSign        = Math.Sign(currentPieceMovableFigureBoardLocation.Y - adjacentPieceMovableFigureBoardLocation.Y);
                                int sourcePictureYDifferenceSign = Math.Sign(currentPiece.SourcePictureLocation.Y - adjacentPiece.SourcePictureLocation.Y);

                                // Adjacent piece is on the wrong side of the current piece. Do not snap to the current piece.
                                // For example, adjacent piece should be located below the current piece instead of being
                                // located above it.
                                if (figureYDifferenceSign != sourcePictureYDifferenceSign)
                                {
                                    continue;
                                }
                            }
                            else if (Math.Abs(currentPiece.SourcePictureLocation.Y - adjacentPiece.SourcePictureLocation.Y) <= 2)
                            {
                                int figureXDifferenceSign      = Math.Sign(currentPieceMovableFigureBoardLocation.X - adjacentPieceMovableFigureBoardLocation.X);
                                int sourceImageXDifferenceSign = Math.Sign(currentPiece.SourcePictureLocation.X - adjacentPiece.SourcePictureLocation.X);

                                // Adjacent piece is on the wrong side of the current piece. Do not snap to the current piece.
                                // For example, adjacent piece should be located at the right side of the current piece instead
                                // of being located at the left side.
                                if (figureXDifferenceSign != sourceImageXDifferenceSign)
                                {
                                    continue;
                                }
                            }

                            #endregion

                            #region Determine if the adjacent piece should be snapped to the current cluster

                            // =================================================
                            // If the dimensions of the rectangle bounds of the merged path
                            // almost equals the "unioned" rectangles of the two pieces' source image
                            // dimensions, they can be snapped together.
                            // =================================================
                            GraphicsPath combinedMovableFigure = new GraphicsPath();
                            combinedMovableFigure.AddPath(adjacentPiece.MovableFigure, false);
                            combinedMovableFigure.AddPath(currentPiece.MovableFigure, false);

                            Rectangle combinedMovableFigureBoardLocation = Rectangle.Truncate(combinedMovableFigure.GetBounds());

                            // The combined rectangle based on the source picture dimensions of the two pieces
                            Rectangle combinedSourcePictureLocation = Rectangle.Union(adjacentPiece.SourcePictureLocation, currentPiece.SourcePictureLocation);

                            if (Math.Abs(combinedMovableFigureBoardLocation.Width - combinedSourcePictureLocation.Width) <= GameSettings.SNAP_TOLERANCE &&
                                Math.Abs(combinedMovableFigureBoardLocation.Height - combinedSourcePictureLocation.Height) <= GameSettings.SNAP_TOLERANCE)
                            {
                                PieceCluster adjacentPieceCluster = GetPieceClusterByID(adjacentPiece.ClusterID);

                                adjacentClusterIDs.Add(adjacentPieceCluster.ID);

                                // Update the ClusterID for the pieces in adjacent cluster
                                foreach (Piece piece in adjacentPieceCluster.Pieces)
                                {
                                    piece.ClusterID = currentPiece.ClusterID;
                                }
                            }

                            #endregion
                        }
                    }
                }

                if (adjacentClusterIDs.Count > 0)
                {
                    #region Remove the adjacent cluster from the list after combining with the current cluster

                    foreach (int clusterID in adjacentClusterIDs)
                    {
                        PieceCluster adjacentCluster = GetPieceClusterByID(clusterID);

                        foreach (Piece piece in adjacentCluster.Pieces)
                        {
                            _currentCluster.Pieces.Add(piece);
                        }

                        RemovePieceGroupByID(clusterID);
                    }

                    #endregion

                    GraphicsPath combinedStaticFigure          = new GraphicsPath();
                    Rectangle    combinedBoardLocation         = _currentCluster.BoardLocation;
                    Rectangle    combinedSourcePictureLocation = _currentCluster.SourcePictureLocation;

                    foreach (Piece piece in _currentCluster.Pieces)
                    {
                        combinedStaticFigure.AddPath(piece.StaticFigure, false);
                        combinedBoardLocation         = Rectangle.Union(combinedBoardLocation, piece.BoardLocation);
                        combinedSourcePictureLocation = Rectangle.Union(combinedSourcePictureLocation, piece.SourcePictureLocation);
                    }

                    _currentCluster.BoardLocation = new Rectangle(combinedBoardLocation.X, combinedBoardLocation.Y,
                                                                  combinedSourcePictureLocation.Width,
                                                                  combinedSourcePictureLocation.Height);

                    _currentCluster.SourcePictureLocation = combinedSourcePictureLocation;
                    _currentCluster.Width         = combinedSourcePictureLocation.Width;
                    _currentCluster.Height        = combinedSourcePictureLocation.Height;
                    _currentCluster.StaticFigure  = (GraphicsPath)combinedStaticFigure.Clone();
                    _currentCluster.MovableFigure = (GraphicsPath)combinedStaticFigure.Clone();

                    Rectangle combinedStaticFigureLocation = Rectangle.Truncate(combinedStaticFigure.GetBounds());

                    // Translate the movable figure to the origin first and...
                    matrix.Reset();
                    matrix.Translate(0 - combinedStaticFigureLocation.X, 0 - combinedStaticFigureLocation.Y);
                    _currentCluster.MovableFigure.Transform(matrix);

                    //  ...then translate to the new board location
                    matrix.Reset();
                    matrix.Translate(combinedBoardLocation.X, combinedBoardLocation.Y);
                    _currentCluster.MovableFigure.Transform(matrix);

                    #region Construct cluster picture

                    // Translate the figure to the origin to draw the picture
                    matrix.Reset();
                    matrix.Translate(0 - combinedStaticFigureLocation.X, 0 - combinedStaticFigureLocation.Y);
                    GraphicsPath translatedCombinedStaticFigure = (GraphicsPath)combinedStaticFigure.Clone();
                    translatedCombinedStaticFigure.Transform(matrix);

                    Bitmap clusterPicture = new Bitmap(combinedSourcePictureLocation.Width, combinedSourcePictureLocation.Height);

                    using (Graphics gfx = Graphics.FromImage(clusterPicture))
                    {
                        gfx.FillRectangle(Brushes.White, 0, 0, clusterPicture.Width, clusterPicture.Height);

                        gfx.ResetClip();
                        gfx.SetClip(translatedCombinedStaticFigure);
                        gfx.DrawImage(_sourcePicture, new Rectangle(0, 0, clusterPicture.Width, clusterPicture.Height),
                                      combinedStaticFigureLocation, GraphicsUnit.Pixel);

                        if (GameSettings.DRAW_PIECE_OUTLINE)
                        {
                            Pen outlinePen = new Pen(Color.Black)
                            {
                                Width     = GameSettings.PIECE_OUTLINE_WIDTH,
                                Alignment = PenAlignment.Inset
                            };

                            gfx.SmoothingMode = SmoothingMode.AntiAlias;
                            gfx.DrawPath(outlinePen, translatedCombinedStaticFigure);
                        }
                    }

                    Bitmap modifiedClusterPicture = (Bitmap)clusterPicture.Clone();
                    ImageUtilities.EdgeDetectHorizontal(modifiedClusterPicture);
                    ImageUtilities.EdgeDetectVertical(modifiedClusterPicture);
                    clusterPicture = ImageUtilities.AlphaBlendMatrix(modifiedClusterPicture, clusterPicture, 200);

                    #endregion

                    _currentCluster.Picture = (Bitmap)clusterPicture.Clone();

                    // Update the piece's movable figure and board location
                    foreach (Piece piece in _currentCluster.Pieces)
                    {
                        int offsetX = piece.SourcePictureLocation.X - combinedSourcePictureLocation.X;
                        int offsetY = piece.SourcePictureLocation.Y - combinedSourcePictureLocation.Y;

                        int newLocationX = combinedBoardLocation.X + offsetX;
                        int newLocationY = combinedBoardLocation.Y + offsetY;

                        piece.BoardLocation = new Rectangle(newLocationX, newLocationY, piece.Width, piece.Height);

                        Rectangle movableFigureBoardLocation = Rectangle.Truncate(piece.MovableFigure.GetBounds());

                        // Translate the movable figure to the origin first and...
                        matrix.Reset();
                        matrix.Translate(0 - movableFigureBoardLocation.X, 0 - movableFigureBoardLocation.Y);
                        piece.MovableFigure.Transform(matrix);

                        //  ...then translate to the new board location
                        matrix.Reset();
                        matrix.Translate(newLocationX, newLocationY);
                        piece.MovableFigure.Transform(matrix);
                    }

                    #region Redraw

                    #region Back buffer

                    Rectangle areaToClear = new Rectangle(combinedBoardLocation.X, combinedBoardLocation.Y,
                                                          combinedBoardLocation.Width + GameSettings.DROP_SHADOW_DEPTH,
                                                          combinedBoardLocation.Height + GameSettings.DROP_SHADOW_DEPTH);

                    using (Graphics gfx = Graphics.FromImage(_backBuffer))
                    {
                        // Clear the area with the background picture first
                        gfx.DrawImage(_background, areaToClear, areaToClear, GraphicsUnit.Pixel);

                        #region Redraw the pieces

                        Region regionToRedraw = new Region(areaToClear);

                        foreach (PieceCluster cluster in _clusters)
                        {
                            Region clusterRegion = new Region(cluster.MovableFigure);
                            clusterRegion.Intersect(regionToRedraw);

                            if (!clusterRegion.IsEmpty(gfx))
                            {
                                gfx.SetClip(clusterRegion, CombineMode.Replace);
                                gfx.DrawImageUnscaled(cluster.Picture, cluster.BoardLocation);
                            }
                        }

                        #endregion
                    }

                    #endregion

                    #region Board

                    using (Graphics gfx = Graphics.FromImage(_board))
                    {
                        gfx.DrawImageUnscaled(_backBuffer, 0, 0);
                    }

                    #endregion

                    #region Form

                    using (Graphics gfx = this.CreateGraphics())
                    {
                        gfx.DrawImageUnscaled(_backBuffer, 0, 0);
                    }

                    #endregion

                    #endregion
                }

                #endregion

                _canMovePiece   = false;
                _currentCluster = null;

                #region Victory announcement

                if (_clusters.Count == 1)
                {
                    if (_victoryAnnounced == false)
                    {
                        _victoryAnnounced = true;
                        MessageBox.Show("The puzzle has been solved!", "Congratulations!", MessageBoxButtons.OK);
                    }
                }

                #endregion
            }
        }
        public List <PieceCluster> CreateJigsawPuzzle(Size clientSize, Image sourcePicture, int menuStripHeight)
        {
            #region Some validation

            if (sourcePicture == null)
            {
                throw new Exception("Please provide source picture.");
            }

            if (GameSettings.NUM_ROWS <= 1 || GameSettings.NUM_COLUMNS <= 1)
            {
                throw new Exception("GameSettings.NUM_COLUMNS and GameSettings.NUM_ROWS must be at least 2.");
            }

            if (GameSettings.NUM_COLUMNS != GameSettings.NUM_ROWS)
            {
                throw new Exception("GameSettings.NUM_COLUMNS and GameSettings.NUM_ROWS must be the same.");
            }

            #endregion

            #region Make sure the piece size is not too small

            int pieceWidth  = sourcePicture.Width / GameSettings.NUM_COLUMNS;
            int pieceHeight = sourcePicture.Height / GameSettings.NUM_ROWS;

            if (pieceWidth < GameSettings.MIN_PIECE_WIDTH || pieceHeight < GameSettings.MIN_PIECE_HEIGHT)
            {
                throw new Exception("The picture is too small. Please select a bigger picture.");
            }

            int lastColPieceWidth  = pieceWidth + (sourcePicture.Width % GameSettings.NUM_COLUMNS);
            int lastRowPieceHeight = pieceHeight + (sourcePicture.Height % GameSettings.NUM_ROWS);

            #endregion

            #region Construct jigsaw pieces

            int lastRow  = (GameSettings.NUM_ROWS - 1);
            int lastCol  = (GameSettings.NUM_COLUMNS - 1);
            var clusters = new List <PieceCluster>();

            Matrix matrix = new Matrix();

            Pen outlinePen = new Pen(Color.Black)
            {
                Width     = GameSettings.PIECE_OUTLINE_WIDTH,
                Alignment = PenAlignment.Inset
            };

            var vsets = new List <Vector3>(5);
            vsets.Add(new Vector3(0.9, 1.1, 0.95));
            vsets.Add(new Vector3(1.1, 0.9, 1.05));
            vsets.Add(new Vector3(1.13, 0.95, 1.0));
            vsets.Add(new Vector3(0.9, 0.9, 1.15));
            vsets.Add(new Vector3(0.95, 1.19, 0.95));
            int[, ,] mat = new int[GameSettings.NUM_ROWS, GameSettings.NUM_COLUMNS, 4];

            int    pieceID = 0;
            Random rnd     = new Random((int)(DateTime.Now.Ticks));

            for (int row = 0; row < GameSettings.NUM_ROWS; row++)
            {
                // Make the top and bottom curves face in the same way or opposite direction.

                for (int col = 0; col < GameSettings.NUM_COLUMNS; col++)
                {
                    mat[row, col, RightCurve]  = rnd.Next(5);
                    mat[row, col, BottomCurve] = rnd.Next(5);
                    mat[row, col, RightOutty]  = rnd.Next(2);
                    mat[row, col, BottomOutty] = rnd.Next(2);

                    GraphicsPath figure = new GraphicsPath();

                    int offsetX = (col * pieceWidth);
                    int offsetY = (row * pieceHeight);
                    int horizontalCurveLength = (col == lastCol ? lastColPieceWidth : pieceWidth);
                    int verticalCurveLength   = (row == lastRow ? lastRowPieceHeight : pieceHeight);
                    #region Top

                    if (row == 0)
                    {
                        int startX = offsetX;
                        int startY = offsetY;
                        int endX   = offsetX + horizontalCurveLength;
                        int endY   = offsetY;

                        figure.AddLine(startX, startY, endX, endY);
                    }
                    else
                    {
                        var v     = vsets[mat[row - 1, col, BottomCurve]];
                        var crTop = new CurveRatio(v.X, v.Y, 2.0 - v.Z);
                        //var crTop = new CurveRatio(v.X, v.Y, v.Z);
                        BezierCurve topCurve = BezierCurve.CreateHorizontal(horizontalCurveLength, crTop);

                        if (mat[row - 1, col, BottomOutty] > 0)
                        {
                            topCurve.FlipVertical();
                        }
                        figure.AddBeziers(topCurve.Translate(offsetX, offsetY).Points);
                    }

                    #endregion

                    #region Right

                    if (col == lastCol)
                    {
                        int startX = offsetX + lastColPieceWidth;
                        int startY = offsetY;
                        int endX   = offsetX + lastColPieceWidth;
                        int endY   = offsetY + verticalCurveLength;

                        figure.AddLine(startX, startY, endX, endY);
                    }
                    else
                    {
                        BezierCurve verticalCurve = BezierCurve.CreateVertical(verticalCurveLength, new CurveRatio(vsets[mat[row, col, RightCurve]]));

                        if (mat[row, col, RightOutty] > 0)
                        {
                            verticalCurve.FlipHorizontal();
                        }

                        verticalCurve.Translate(offsetX + pieceWidth, offsetY);
                        figure.AddBeziers(verticalCurve.Points);
                    }

                    #endregion

                    #region Bottom

                    if (row == lastRow)
                    {
                        int startX = offsetX;
                        int startY = offsetY + lastRowPieceHeight;
                        int endX   = offsetX + horizontalCurveLength;
                        int endY   = offsetY + lastRowPieceHeight;

                        figure.AddLine(endX, endY, startX, startY);
                    }
                    else
                    {
                        BezierCurve bottomCurve = BezierCurve.CreateHorizontal(horizontalCurveLength, new CurveRatio(vsets[mat[row, col, BottomCurve]]));
                        bottomCurve.FlipHorizontal();

                        if (mat[row, col, BottomOutty] > 0)
                        {
                            bottomCurve.FlipVertical();
                        }

                        bottomCurve.Translate(offsetX + horizontalCurveLength, offsetY + pieceHeight);
                        figure.AddBeziers(bottomCurve.Points);
                    }

                    #endregion

                    #region Left

                    if (col == 0)
                    {
                        int startX = offsetX;
                        int startY = offsetY;
                        int endX   = offsetX;
                        int endY   = offsetY + verticalCurveLength;

                        figure.AddLine(endX, endY, startX, startY);
                    }
                    else
                    {
                        var v      = vsets[mat[row, col - 1, RightCurve]];
                        var crLeft = new CurveRatio(v.X, v.Y, 2.0 - v.Z);
                        //var crLeft = new CurveRatio(v.X, v.Y, v.Z);
                        BezierCurve verticalCurve = BezierCurve.CreateVertical(verticalCurveLength, crLeft);
                        verticalCurve.FlipVertical();

                        if (mat[row, col - 1, RightOutty] > 0)
                        {
                            verticalCurve.FlipHorizontal();
                        }

                        verticalCurve.Translate(offsetX, offsetY + verticalCurveLength);
                        figure.AddBeziers(verticalCurve.Points);
                    }

                    #endregion

                    #region Jigsaw information

                    #region Determine adjacent piece IDs for the current piece

                    List <Coordinate> adjacentCoords = new List <Coordinate>
                    {
                        new Coordinate(col, row - 1),
                        new Coordinate(col + 1, row),
                        new Coordinate(col, row + 1),
                        new Coordinate(col - 1, row)
                    };

                    List <int> adjacentPieceIDs = DetermineAdjacentPieceIDs(adjacentCoords, GameSettings.NUM_COLUMNS);

                    #endregion

                    #region Construct piece picture

                    Rectangle figureLocation = Rectangle.Truncate(figure.GetBounds());

                    // Translate the figure to the origin to draw the picture for individual piece
                    matrix.Reset();
                    matrix.Translate(0 - figureLocation.X, 0 - figureLocation.Y);
                    GraphicsPath translatedFigure = (GraphicsPath)figure.Clone();
                    translatedFigure.Transform(matrix);

                    Rectangle translatedFigureLocation = Rectangle.Truncate(translatedFigure.GetBounds());

                    Bitmap piecePicture = new Bitmap(figureLocation.Width, figureLocation.Height);

                    using (Graphics gfx = Graphics.FromImage(piecePicture))
                    {
                        gfx.FillRectangle(Brushes.White, 0, 0, piecePicture.Width, piecePicture.Height);
                        gfx.ResetClip();
                        gfx.SetClip(translatedFigure);
                        gfx.DrawImage(sourcePicture, new Rectangle(0, 0, piecePicture.Width, piecePicture.Height),
                                      figureLocation, GraphicsUnit.Pixel);

                        if (GameSettings.DRAW_PIECE_OUTLINE)
                        {
                            gfx.SmoothingMode = SmoothingMode.AntiAlias;
                            gfx.DrawPath(outlinePen, translatedFigure);
                        }
                    }

                    // Wanted to do the "bevel edge" effect but too complicated for me.
                    Bitmap modifiedPiecePicture = (Bitmap)piecePicture.Clone();
                    ImageUtilities.EdgeDetectHorizontal(modifiedPiecePicture);
                    ImageUtilities.EdgeDetectVertical(modifiedPiecePicture);
                    piecePicture = ImageUtilities.AlphaBlendMatrix(modifiedPiecePicture, piecePicture, 200);

                    #endregion

                    #region Piece and cluster information

                    Piece piece = new Piece
                    {
                        ID                    = pieceID,
                        ClusterID             = pieceID,
                        Width                 = figureLocation.Width,
                        Height                = figureLocation.Height,
                        BoardLocation         = translatedFigureLocation,
                        SourcePictureLocation = figureLocation,
                        MovableFigure         = (GraphicsPath)translatedFigure.Clone(),
                        StaticFigure          = (GraphicsPath)figure.Clone(),
                        Picture               = (Bitmap)piecePicture.Clone(),
                        AdjacentPieceIDs      = adjacentPieceIDs
                    };

                    PieceCluster cluster = new PieceCluster
                    {
                        ID                    = pieceID,
                        Width                 = figureLocation.Width,
                        Height                = figureLocation.Height,
                        BoardLocation         = translatedFigureLocation,
                        SourcePictureLocation = figureLocation,
                        MovableFigure         = (GraphicsPath)translatedFigure.Clone(),
                        StaticFigure          = (GraphicsPath)figure.Clone(),
                        Picture               = (Bitmap)piecePicture.Clone(),
                        Pieces                = new List <Piece> {
                            piece
                        }
                    };

                    #endregion

                    clusters.Add(cluster);

                    #endregion

                    pieceID++;
                }
            }

            //var ratioLeft = new CurveRatio(1.1, 0.9, 1.05);
            //var ratioRight = new CurveRatio(1.1, 0.9, 0.95);
            //var ratioTop = new CurveRatio(0.9, 1.1, 1.0);
            //var ratioBottom = new CurveRatio(0.9, 1.1, 1.0);

            //for (int row = 0; row < GameSettings.NUM_ROWS; row++)
            //{
            //   // Make the top and bottom curves face in the same way or opposite direction.
            //   bool topCurveFlipVertical = (row % 2 == 0);
            //   bool bottomCurveFlipVertical = (row % 2 != 0);

            //   for (int col = 0; col < GameSettings.NUM_COLUMNS; col++)
            //   {
            //      // Make the left and right curves face in the same way or opposite direction.
            //      bool leftCurveFlipHorizontal = (col % 2 != 0);
            //      bool rightCurveFlipHorizontal = (col % 2 == 0);

            //      // Toggle the facing of left and right curves with each row.
            //      if (row % 2 == 0)
            //      {
            //         leftCurveFlipHorizontal = (col % 2 == 0);
            //         rightCurveFlipHorizontal = (col % 2 != 0);
            //      }

            //      // Toggle the facing of top and bottom curves with each row.
            //      topCurveFlipVertical = !topCurveFlipVertical;
            //      bottomCurveFlipVertical = !bottomCurveFlipVertical;

            //      GraphicsPath figure = new GraphicsPath();

            //      int offsetX = (col * pieceWidth);
            //      int offsetY = (row * pieceHeight);
            //      int horizontalCurveLength = (col == lastCol ? lastColPieceWidth : pieceWidth);
            //      int verticalCurveLength = (row == lastRow ? lastRowPieceHeight : pieceHeight);

            //      #region Top

            //      if (row == 0)
            //      {
            //         int startX = offsetX;
            //         int startY = offsetY;
            //         int endX = offsetX + horizontalCurveLength;
            //         int endY = offsetY;

            //         figure.AddLine(startX, startY, endX, endY);
            //      }
            //      else
            //      {
            //         BezierCurve topCurve = BezierCurve.CreateHorizontal(horizontalCurveLength, ratioTop);

            //         if (topCurveFlipVertical)
            //         {
            //            topCurve.FlipVertical();
            //         }

            //         topCurve.Translate(offsetX, offsetY);
            //         figure.AddBeziers(topCurve.Points);
            //      }

            //      #endregion

            //      #region Right

            //      if (col == lastCol)
            //      {
            //         int startX = offsetX + lastColPieceWidth;
            //         int startY = offsetY;
            //         int endX = offsetX + lastColPieceWidth;
            //         int endY = offsetY + verticalCurveLength;

            //         figure.AddLine(startX, startY, endX, endY);
            //      }
            //      else
            //      {
            //         BezierCurve verticalCurve = BezierCurve.CreateVertical(verticalCurveLength, ratioRight);

            //         if (rightCurveFlipHorizontal)
            //         {
            //            verticalCurve.FlipHorizontal();
            //         }

            //         verticalCurve.Translate(offsetX + pieceWidth, offsetY);
            //         figure.AddBeziers(verticalCurve.Points);
            //      }

            //      #endregion

            //      #region Bottom

            //      if (row == lastRow)
            //      {
            //         int startX = offsetX;
            //         int startY = offsetY + lastRowPieceHeight;
            //         int endX = offsetX + horizontalCurveLength;
            //         int endY = offsetY + lastRowPieceHeight;

            //         figure.AddLine(endX, endY, startX, startY);
            //      }
            //      else
            //      {
            //         BezierCurve bottomCurve = BezierCurve.CreateHorizontal(horizontalCurveLength, ratioBottom);
            //         bottomCurve.FlipHorizontal();

            //         if (bottomCurveFlipVertical)
            //         {
            //            bottomCurve.FlipVertical();
            //         }

            //         bottomCurve.Translate(offsetX + horizontalCurveLength, offsetY + pieceHeight);
            //         figure.AddBeziers(bottomCurve.Points);
            //      }

            //      #endregion

            //      #region Left

            //      if (col == 0)
            //      {
            //         int startX = offsetX;
            //         int startY = offsetY;
            //         int endX = offsetX;
            //         int endY = offsetY + verticalCurveLength;

            //         figure.AddLine(endX, endY, startX, startY);
            //      }
            //      else
            //      {
            //         BezierCurve verticalCurve = BezierCurve.CreateVertical(verticalCurveLength, ratioLeft);
            //         verticalCurve.FlipVertical();

            //         if (leftCurveFlipHorizontal)
            //         {
            //            verticalCurve.FlipHorizontal();
            //         }

            //         verticalCurve.Translate(offsetX, offsetY + verticalCurveLength);
            //         figure.AddBeziers(verticalCurve.Points);
            //      }

            //      #endregion

            //      #region Jigsaw information

            //      #region Determine adjacent piece IDs for the current piece

            //      List<Coordinate> adjacentCoords = new List<Coordinate>
            //           {
            //               new Coordinate(col, row - 1),
            //               new Coordinate(col + 1, row),
            //               new Coordinate(col, row + 1),
            //               new Coordinate(col - 1, row)
            //           };

            //      List<int> adjacentPieceIDs = DetermineAdjacentPieceIDs(adjacentCoords, GameSettings.NUM_COLUMNS);

            //      #endregion

            //      #region Construct piece picture

            //      Rectangle figureLocation = Rectangle.Truncate(figure.GetBounds());

            //      // Translate the figure to the origin to draw the picture for individual piece
            //      matrix.Reset();
            //      matrix.Translate(0 - figureLocation.X, 0 - figureLocation.Y);
            //      GraphicsPath translatedFigure = (GraphicsPath)figure.Clone();
            //      translatedFigure.Transform(matrix);

            //      Rectangle translatedFigureLocation = Rectangle.Truncate(translatedFigure.GetBounds());

            //      Bitmap piecePicture = new Bitmap(figureLocation.Width, figureLocation.Height);

            //      using (Graphics gfx = Graphics.FromImage(piecePicture))
            //      {
            //         gfx.FillRectangle(Brushes.White, 0, 0, piecePicture.Width, piecePicture.Height);
            //         gfx.ResetClip();
            //         gfx.SetClip(translatedFigure);
            //         gfx.DrawImage(sourcePicture, new Rectangle(0, 0, piecePicture.Width, piecePicture.Height),
            //                 figureLocation, GraphicsUnit.Pixel);

            //         if (GameSettings.DRAW_PIECE_OUTLINE)
            //         {
            //            gfx.SmoothingMode = SmoothingMode.AntiAlias;
            //            gfx.DrawPath(outlinePen, translatedFigure);
            //         }
            //      }

            //      // Wanted to do the "bevel edge" effect but too complicated for me.
            //      Bitmap modifiedPiecePicture = (Bitmap)piecePicture.Clone();
            //      ImageUtilities.EdgeDetectHorizontal(modifiedPiecePicture);
            //      ImageUtilities.EdgeDetectVertical(modifiedPiecePicture);
            //      piecePicture = ImageUtilities.AlphaBlendMatrix(modifiedPiecePicture, piecePicture, 200);

            //      #endregion

            //      #region Piece and cluster information

            //      Piece piece = new Piece
            //      {
            //         ID = pieceID,
            //         ClusterID = pieceID,
            //         Width = figureLocation.Width,
            //         Height = figureLocation.Height,
            //         BoardLocation = translatedFigureLocation,
            //         SourcePictureLocation = figureLocation,
            //         MovableFigure = (GraphicsPath)translatedFigure.Clone(),
            //         StaticFigure = (GraphicsPath)figure.Clone(),
            //         Picture = (Bitmap)piecePicture.Clone(),
            //         AdjacentPieceIDs = adjacentPieceIDs
            //      };

            //      PieceCluster cluster = new PieceCluster
            //      {
            //         ID = pieceID,
            //         Width = figureLocation.Width,
            //         Height = figureLocation.Height,
            //         BoardLocation = translatedFigureLocation,
            //         SourcePictureLocation = figureLocation,
            //         MovableFigure = (GraphicsPath)translatedFigure.Clone(),
            //         StaticFigure = (GraphicsPath)figure.Clone(),
            //         Picture = (Bitmap)piecePicture.Clone(),
            //         Pieces = new List<Piece> { piece }
            //      };

            //      #endregion

            //      clusters.Add(cluster);

            //      #endregion

            //      pieceID++;
            //   }
            //}

            #endregion

            #region Scramble jigsaw pieces

            Random random = new Random();

            int boardWidth  = clientSize.Width;
            int boardHeight = clientSize.Height;

            foreach (PieceCluster cluster in clusters)
            {
                int locationX = random.Next(1, boardWidth);
                int locationY = random.Next((menuStripHeight + 1), boardHeight);

                #region Make sure the piece is within client rectangle bounds

                if ((locationX + cluster.Width) > boardWidth)
                {
                    locationX = locationX - ((locationX + cluster.Width) - boardWidth);
                }

                if ((locationY + cluster.Height) > boardHeight)
                {
                    locationY = locationY - ((locationY + cluster.Height) - boardHeight);
                }

                #endregion

                for (int index = 0; index < cluster.Pieces.Count; index++)
                {
                    Piece piece = cluster.Pieces[index];
                    piece.BoardLocation = new Rectangle(locationX, locationY, piece.Width, piece.Height);

                    matrix.Reset();
                    matrix.Translate(locationX, locationY);
                    piece.MovableFigure.Transform(matrix);
                }

                // Move the figure for cluster piece
                cluster.BoardLocation = new Rectangle(locationX, locationY, cluster.Width, cluster.Height);

                matrix.Reset();
                matrix.Translate(locationX, locationY);
                cluster.MovableFigure.Transform(matrix);
            }
            return(clusters);
        }
Example #6
0
        private void CreateJigsawPuzzle()
        {
            #region Some validation

            if (_sourcePicture == null)
            {
                throw new Exception("Please provide source picture.");
            }

            if (GameSettings.NUM_ROWS <= 1 || GameSettings.NUM_COLUMNS <= 1)
            {
                throw new Exception("GameSettings.NUM_COLUMNS and GameSettings.NUM_ROWS must be at least 2.");
            }

            if (GameSettings.NUM_COLUMNS != GameSettings.NUM_ROWS)
            {
                throw new Exception("GameSettings.NUM_COLUMNS and GameSettings.NUM_ROWS must be the same.");
            }

            #endregion

            #region Make sure the piece size is not too small

            int pieceWidth  = _sourcePicture.Width / GameSettings.NUM_COLUMNS;
            int pieceHeight = _sourcePicture.Height / GameSettings.NUM_ROWS;

            if (pieceWidth < GameSettings.MIN_PIECE_WIDTH || pieceHeight < GameSettings.MIN_PIECE_HEIGHT)
            {
                throw new Exception("The picture is too small. Please select a bigger picture.");
            }

            int lastColPieceWidth  = pieceWidth + (_sourcePicture.Width % GameSettings.NUM_COLUMNS);
            int lastRowPieceHeight = pieceHeight + (_sourcePicture.Height % GameSettings.NUM_ROWS);

            #endregion

            #region Construct jigsaw pieces

            int lastRow = (GameSettings.NUM_ROWS - 1);
            int lastCol = (GameSettings.NUM_COLUMNS - 1);

            _currentCluster = null;
            _clusters       = new List <PieceCluster>();

            Matrix matrix = new Matrix();

            Pen outlinePen = new Pen(Color.Black)
            {
                Width     = GameSettings.PIECE_OUTLINE_WIDTH,
                Alignment = PenAlignment.Inset
            };

            int pieceID = 0;

            for (int row = 0; row < GameSettings.NUM_ROWS; row++)
            {
                // Make the top and bottom curves face in the same way or opposite direction.
                bool topCurveFlipVertical    = (row % 2 == 0);
                bool bottomCurveFlipVertical = (row % 2 != 0);

                for (int col = 0; col < GameSettings.NUM_COLUMNS; col++)
                {
                    // Make the left and right curves face in the same way or opposite direction.
                    bool leftCurveFlipHorizontal  = (col % 2 != 0);
                    bool rightCurveFlipHorizontal = (col % 2 == 0);

                    // Toggle the facing of left and right curves with each row.
                    if (row % 2 == 0)
                    {
                        leftCurveFlipHorizontal  = (col % 2 == 0);
                        rightCurveFlipHorizontal = (col % 2 != 0);
                    }

                    // Toggle the facing of top and bottom curves with each row.
                    topCurveFlipVertical    = !topCurveFlipVertical;
                    bottomCurveFlipVertical = !bottomCurveFlipVertical;

                    GraphicsPath figure = new GraphicsPath();

                    int offsetX = (col * pieceWidth);
                    int offsetY = (row * pieceHeight);
                    int horizontalCurveLength = (col == lastCol ? lastColPieceWidth : pieceWidth);
                    int verticalCurveLength   = (row == lastRow ? lastRowPieceHeight : pieceHeight);

                    #region Top

                    if (row == 0)
                    {
                        int startX = offsetX;
                        int startY = offsetY;
                        int endX   = offsetX + horizontalCurveLength;
                        int endY   = offsetY;

                        figure.AddLine(startX, startY, endX, endY);
                    }
                    else
                    {
                        BezierCurve topCurve = BezierCurve.CreateHorizontal(horizontalCurveLength);

                        if (topCurveFlipVertical)
                        {
                            topCurve.FlipVertical();
                        }

                        topCurve.Translate(offsetX, offsetY);
                        figure.AddBeziers(topCurve.Points);
                    }

                    #endregion

                    #region Right

                    if (col == lastCol)
                    {
                        int startX = offsetX + lastColPieceWidth;
                        int startY = offsetY;
                        int endX   = offsetX + lastColPieceWidth;
                        int endY   = offsetY + verticalCurveLength;

                        figure.AddLine(startX, startY, endX, endY);
                    }
                    else
                    {
                        BezierCurve verticalCurve = BezierCurve.CreateVertical(verticalCurveLength);

                        if (rightCurveFlipHorizontal)
                        {
                            verticalCurve.FlipHorizontal();
                        }

                        verticalCurve.Translate(offsetX + pieceWidth, offsetY);
                        figure.AddBeziers(verticalCurve.Points);
                    }

                    #endregion

                    #region Bottom

                    if (row == lastRow)
                    {
                        int startX = offsetX;
                        int startY = offsetY + lastRowPieceHeight;
                        int endX   = offsetX + horizontalCurveLength;
                        int endY   = offsetY + lastRowPieceHeight;

                        figure.AddLine(endX, endY, startX, startY);
                    }
                    else
                    {
                        BezierCurve bottomCurve = BezierCurve.CreateHorizontal(horizontalCurveLength);
                        bottomCurve.FlipHorizontal();

                        if (bottomCurveFlipVertical)
                        {
                            bottomCurve.FlipVertical();
                        }

                        bottomCurve.Translate(offsetX + horizontalCurveLength, offsetY + pieceHeight);
                        figure.AddBeziers(bottomCurve.Points);
                    }

                    #endregion

                    #region Left

                    if (col == 0)
                    {
                        int startX = offsetX;
                        int startY = offsetY;
                        int endX   = offsetX;
                        int endY   = offsetY + verticalCurveLength;

                        figure.AddLine(endX, endY, startX, startY);
                    }
                    else
                    {
                        BezierCurve verticalCurve = BezierCurve.CreateVertical(verticalCurveLength);
                        verticalCurve.FlipVertical();

                        if (leftCurveFlipHorizontal)
                        {
                            verticalCurve.FlipHorizontal();
                        }

                        verticalCurve.Translate(offsetX, offsetY + verticalCurveLength);
                        figure.AddBeziers(verticalCurve.Points);
                    }

                    #endregion

                    #region Jigsaw information

                    #region Determine adjacent piece IDs for the current piece

                    List <Coordinate> adjacentCoords = new List <Coordinate>
                    {
                        new Coordinate(col, row - 1),
                        new Coordinate(col + 1, row),
                        new Coordinate(col, row + 1),
                        new Coordinate(col - 1, row)
                    };

                    List <int> adjacentPieceIDs = DetermineAdjacentPieceIDs(adjacentCoords, GameSettings.NUM_COLUMNS);

                    #endregion

                    #region Construct piece picture

                    Rectangle figureLocation = Rectangle.Truncate(figure.GetBounds());

                    // Translate the figure to the origin to draw the picture for individual piece
                    matrix.Reset();
                    matrix.Translate(0 - figureLocation.X, 0 - figureLocation.Y);
                    GraphicsPath translatedFigure = (GraphicsPath)figure.Clone();
                    translatedFigure.Transform(matrix);

                    Rectangle translatedFigureLocation = Rectangle.Truncate(translatedFigure.GetBounds());

                    Bitmap piecePicture = new Bitmap(figureLocation.Width, figureLocation.Height);

                    using (Graphics gfx = Graphics.FromImage(piecePicture))
                    {
                        gfx.FillRectangle(Brushes.White, 0, 0, piecePicture.Width, piecePicture.Height);
                        gfx.ResetClip();
                        gfx.SetClip(translatedFigure);
                        gfx.DrawImage(_sourcePicture, new Rectangle(0, 0, piecePicture.Width, piecePicture.Height),
                                      figureLocation, GraphicsUnit.Pixel);

                        if (GameSettings.DRAW_PIECE_OUTLINE)
                        {
                            gfx.SmoothingMode = SmoothingMode.AntiAlias;
                            gfx.DrawPath(outlinePen, translatedFigure);
                        }
                    }

                    // Wanted to do the "bevel edge" effect but too complicated for me.
                    Bitmap modifiedPiecePicture = (Bitmap)piecePicture.Clone();
                    ImageUtilities.EdgeDetectHorizontal(modifiedPiecePicture);
                    ImageUtilities.EdgeDetectVertical(modifiedPiecePicture);
                    piecePicture = ImageUtilities.AlphaBlendMatrix(modifiedPiecePicture, piecePicture, 200);

                    #endregion

                    #region Piece and cluster information

                    Piece piece = new Piece
                    {
                        ID                    = pieceID,
                        ClusterID             = pieceID,
                        Width                 = figureLocation.Width,
                        Height                = figureLocation.Height,
                        BoardLocation         = translatedFigureLocation,
                        SourcePictureLocation = figureLocation,
                        MovableFigure         = (GraphicsPath)translatedFigure.Clone(),
                        StaticFigure          = (GraphicsPath)figure.Clone(),
                        Picture               = (Bitmap)piecePicture.Clone(),
                        AdjacentPieceIDs      = adjacentPieceIDs
                    };

                    PieceCluster cluster = new PieceCluster
                    {
                        ID                    = pieceID,
                        Width                 = figureLocation.Width,
                        Height                = figureLocation.Height,
                        BoardLocation         = translatedFigureLocation,
                        SourcePictureLocation = figureLocation,
                        MovableFigure         = (GraphicsPath)translatedFigure.Clone(),
                        StaticFigure          = (GraphicsPath)figure.Clone(),
                        Picture               = (Bitmap)piecePicture.Clone(),
                        Pieces                = new List <Piece> {
                            piece
                        }
                    };

                    #endregion

                    _clusters.Add(cluster);

                    #endregion

                    pieceID++;
                }
            }

            #endregion

            #region Scramble jigsaw pieces

            Random random = new Random();

            int boardWidth  = this.ClientSize.Width;
            int boardHeight = this.ClientSize.Height;

            foreach (PieceCluster cluster in _clusters)
            {
                int locationX = random.Next(1, boardWidth);
                int locationY = random.Next((menuStrip1.Height + 1), boardHeight);

                #region Make sure the piece is within client rectangle bounds

                if ((locationX + cluster.Width) > boardWidth)
                {
                    locationX = locationX - ((locationX + cluster.Width) - boardWidth);
                }

                if ((locationY + cluster.Height) > boardHeight)
                {
                    locationY = locationY - ((locationY + cluster.Height) - boardHeight);
                }

                #endregion

                for (int index = 0; index < cluster.Pieces.Count; index++)
                {
                    Piece piece = cluster.Pieces[index];
                    piece.BoardLocation = new Rectangle(locationX, locationY, piece.Width, piece.Height);

                    matrix.Reset();
                    matrix.Translate(locationX, locationY);
                    piece.MovableFigure.Transform(matrix);
                }

                // Move the figure for cluster piece
                cluster.BoardLocation = new Rectangle(locationX, locationY, cluster.Width, cluster.Height);

                matrix.Reset();
                matrix.Translate(locationX, locationY);
                cluster.MovableFigure.Transform(matrix);
            }

            #endregion
        }