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); } } } }
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); } }
private void MainForm_MouseDown(object sender, MouseEventArgs e) { int selectedIndex = -1; for (int index = (_clusters.Count - 1); index >= 0; index--) { if (_clusters[index].MovableFigure.IsVisible(e.X, e.Y)) { selectedIndex = index; break; } } if (selectedIndex >= 0) { _currentCluster = _clusters[selectedIndex]; _clusters.RemoveAt(selectedIndex); _clusters.Add(_currentCluster); using (Graphics gfx = Graphics.FromImage(_backBuffer)) { Rectangle currentClusterBoardLocation = _currentCluster.BoardLocation; gfx.DrawImage(_background, currentClusterBoardLocation, currentClusterBoardLocation, GraphicsUnit.Pixel); Region currentClusterBoardLocationRegion = new Region(_currentCluster.BoardLocation); foreach (PieceCluster cluster in _clusters) { if (cluster != _currentCluster) { Region clusterRegion = new Region(cluster.MovableFigure); clusterRegion.Intersect(currentClusterBoardLocationRegion); if (!clusterRegion.IsEmpty(gfx)) { gfx.SetClip(clusterRegion, CombineMode.Replace); gfx.DrawImageUnscaled(cluster.Picture, cluster.BoardLocation); } } } } Matrix matrix = new Matrix(); SolidBrush shadowBrush = new SolidBrush(GameSettings.DROP_SHADOW_COLOR); using (Graphics gfx = Graphics.FromImage(_board)) { matrix.Reset(); matrix.Translate(GameSettings.DROP_SHADOW_DEPTH, GameSettings.DROP_SHADOW_DEPTH); GraphicsPath shadowFigure = (GraphicsPath)_currentCluster.MovableFigure.Clone(); shadowFigure.Transform(matrix); gfx.ResetClip(); gfx.SetClip(shadowFigure); gfx.FillPath(shadowBrush, shadowFigure); gfx.ResetClip(); gfx.SetClip(_currentCluster.MovableFigure); gfx.DrawImageUnscaled(_currentCluster.Picture, _currentCluster.BoardLocation); } using (Graphics gfx = this.CreateGraphics()) { gfx.DrawImageUnscaled(_board, 0, 0); } _previousMouseX = e.X; _previousMouseY = e.Y; _canMovePiece = true; } }
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 } }
private void MainForm_MouseDown(object sender, MouseEventArgs e) { #region Determine which cluster is selected (mouse down) int selectedIndex = -1; for (int index = (_clusters.Count - 1); index >= 0; index--) { if (_clusters[index].MovableFigure.IsVisible(e.X, e.Y)) { selectedIndex = index; break; } } #endregion #region Bring up the selected cluster if (selectedIndex >= 0) { _currentCluster = _clusters[selectedIndex]; // Make the current cluster top-most and modify the list to reflect it _clusters.RemoveAt(selectedIndex); _clusters.Add(_currentCluster); #region Back buffer // ======================================================== // Clear the current moving piece's area with the background image and redraw the // pieces whose region intersect with the area. // ======================================================== using (Graphics gfx = Graphics.FromImage(_backBuffer)) { Rectangle currentClusterBoardLocation = _currentCluster.BoardLocation; // Clear the area with the background picture first gfx.DrawImage(_background, currentClusterBoardLocation, currentClusterBoardLocation, GraphicsUnit.Pixel); #region Redraw the pieces Region currentClusterBoardLocationRegion = new Region(_currentCluster.BoardLocation); foreach (PieceCluster cluster in _clusters) { if (cluster != _currentCluster) { Region clusterRegion = new Region(cluster.MovableFigure); clusterRegion.Intersect(currentClusterBoardLocationRegion); if (!clusterRegion.IsEmpty(gfx)) { gfx.SetClip(clusterRegion, CombineMode.Replace); gfx.DrawImageUnscaled(cluster.Picture, cluster.BoardLocation); } } } #endregion } #endregion #region Board Matrix matrix = new Matrix(); SolidBrush shadowBrush = new SolidBrush(GameSettings.DROP_SHADOW_COLOR); using (Graphics gfx = Graphics.FromImage(_board)) { #region Drop shadow // Simple drop shadow only for now. Alpha-blended drop shadow is too slow and jerky. matrix.Reset(); matrix.Translate(GameSettings.DROP_SHADOW_DEPTH, GameSettings.DROP_SHADOW_DEPTH); GraphicsPath shadowFigure = (GraphicsPath)_currentCluster.MovableFigure.Clone(); shadowFigure.Transform(matrix); gfx.ResetClip(); gfx.SetClip(shadowFigure); gfx.FillPath(shadowBrush, shadowFigure); #endregion #region Cluster picture gfx.ResetClip(); gfx.SetClip(_currentCluster.MovableFigure); gfx.DrawImageUnscaled(_currentCluster.Picture, _currentCluster.BoardLocation); #endregion } #endregion #region Form using (Graphics gfx = this.CreateGraphics()) { gfx.DrawImageUnscaled(_board, 0, 0); } #endregion _previousMouseX = e.X; _previousMouseY = e.Y; _canMovePiece = true; } #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); }
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 }