private static void Apply(ExplicitOutlineShape target, TileGridElement tile, int xRight, int yTop, bool allLocations) { // Determine location of the center tile. int x = xRight; int y = yTop; // Determine bounding box of the center tile. Rectangle bbox = tile.BoundingBox; bbox.X += x; bbox.Y += y; // Move tile location to the left/top so that the bounding box is just inside of the shape. int i = bbox.Right / tile.Width; int j = bbox.Bottom / tile.Height; int k = (i + j) % 2; x -= i * tile.Width; y -= j * tile.Height; bbox.X -= i * tile.Width; bbox.Y -= j * tile.Height; // Apply the tile in all locations where the bounding box is still inside of the shape. for (i = 0; bbox.Left + i * tile.Width < target.XSize; i++) { for (j = 0; bbox.Top + j * tile.Height < target.YSize; j++) { if (allLocations || (i + j + k) % 2 == 0) { tile.Apply(target, x + i * tile.Width, y + j * tile.Height); } } } }
/// <summary> /// Apply the tile element in a repeating grid on the whole target shape. /// One tile (which will be inverted if invertEveryOtherTile is true) will be placed at the given location. /// </summary> /// <param name="target"></param> /// <param name="xLeft"></param> /// <param name="yTop"></param> public override void Apply(ExplicitOutlineShape target, int xLeft, int yTop) { Apply(target, tile, xLeft, yTop, true); if (invertEveryOtherTile) { Apply(target, white, xLeft, yTop, false); } }
/// <summary> /// Create an outline shape from the given character and font family. /// </summary> /// <param name="xSize">width of the created shape</param> /// <param name="ySize">height of the created shape</param> /// <param name="centerX">X coordinate, relative to total width; 0.0 = top, 1.0 = bottom</param> /// <param name="centerY">Y coordinate, relative to total height; 0.0 = left, 1.0 = right</param> /// <param name="shapeSize">size, relative to distance of center from the border; 1.0 will touch the border</param> /// <param name="ch"></param> /// <param name="fontFamily"></param> /// <returns></returns> internal static OutlineShape Char(int xSize, int ySize, double centerX, double centerY, double shapeSize, char ch, FontFamily fontFamily) { ExplicitOutlineShape result = new ExplicitOutlineShape(xSize, ySize); double xc, yc, sz; ConvertParameters(xSize, ySize, centerX, centerY, shapeSize, out xc, out yc, out sz); sz *= 2; // sz is not used as a radius but as the character height #region Draw the given character into an image (white on black). StringFormat stringFormat = new StringFormat(); stringFormat.Alignment = StringAlignment.Center; stringFormat.LineAlignment = StringAlignment.Center; int enlargement = 3; int imgXSize = enlargement * xSize, imgYSize = enlargement * ySize; Bitmap img = new Bitmap(imgXSize, imgYSize); Graphics g = Graphics.FromImage(img); Font font = new Font(fontFamily, (float)(enlargement * sz), FontStyle.Bold); g.DrawRectangle(Pens.Black, 0, 0, imgXSize, imgXSize); g.DrawString(new string(ch, 1), font, Brushes.White, new RectangleF(0, 0, imgXSize, (int)(1.20 * imgYSize)), stringFormat); #endregion #region Scale the image so that the covered area is of the requested size. int imgXCenter, imgYCenter; ScaleImage(ref img, sz, out imgXCenter, out imgYCenter); int imgXOffset = imgXCenter - (int)xc; int imgYOffset = imgYCenter - (int)yc; #endregion for (int x = 0; x < xSize; x++) { for (int y = 0; y < ySize; y++) { int imgX = x + imgXOffset, imgY = y + imgYOffset; if (imgX < 0 || imgX >= img.Width || imgY < 0 || imgY >= img.Height) { // result.SetValue(x, y, false); } else if (img.GetPixel(imgX, imgY).GetBrightness() > 0.5) { result.SetValue(x, y, true); } } } return(result); }
/// <summary> /// Create an outline shape. /// </summary> /// <param name="r">a source of random numbers</param> /// <param name="xSize">width of the created shape</param> /// <param name="ySize">height of the created shape</param> /// <param name="centerX">X coordinate, relative to total width; 0.0 = top, 1.0 = bottom</param> /// <param name="centerY">Y coordinate, relative to total height; 0.0 = left, 1.0 = right</param> /// <param name="shapeSize">size, relative to distance of center from the border; 1.0 will touch the border</param> /// <returns></returns> public static OutlineShape Character(Random r, int xSize, int ySize, double centerX, double centerY, double shapeSize) { FontFamily fontFamily = new FontFamily("Helvetica"); char[] shapeCharacters = { 'C', 'O', 'S', 'V', 'X', // no vertical or horizontal lines '3', '6', '8', '9', '?', // no vertical or horizontal lines 'K', 'R', 'Z', 'A', 'G', // some vertical or horizontal lines }; char ch = shapeCharacters[r.Next(shapeCharacters.Length)]; return(ExplicitOutlineShape.Char(xSize, ySize, centerX, centerY, shapeSize, ch, fontFamily)); }
/// <summary> /// The current GridElement is applied to the given target. /// Every set square is inverted. /// </summary> /// <param name="target"></param> /// <param name="xLeft"></param> /// <param name="yTop"></param> public override void Apply(ExplicitOutlineShape target, int xLeft, int yTop) { Rectangle bbox = this.BoundingBox; for (int x = bbox.Left; x < bbox.Right; x++) { for (int y = bbox.Top; y < bbox.Bottom; y++) { if (this[x, y] == true) { this.Invert(target, x + xLeft, y + yTop); } } } }
/// <summary> /// Constructor. /// </summary> /// <param name="xSize">The overall shape size.</param> /// <param name="ySize">The overall shape size.</param> /// <param name="tile">A shape that will be used as the repeating tile pattern.</param> private TilesOutlineShape(int xSize, int ySize, ExplicitOutlineShape tile) : base(xSize, ySize) { this.tile = tile; this.xRepetitions = new int[tile.XSize]; this.yRepetitions = new int[tile.YSize]; for (int i = 0; i < xRepetitions.Length; i++) { xRepetitions[i] = 1; } for (int i = 0; i < yRepetitions.Length; i++) { yRepetitions[i] = 1; } UpdateTileSize(); }
/// <summary> /// Builds a pattern from the given bitmap, rotated and scaled. /// </summary> /// <param name="xSize"></param> /// <param name="ySize"></param> /// <param name="bitmap"></param> /// <param name="rft"></param> /// <param name="scale"></param> /// <returns></returns> private static OutlineShape FromBitmap(int xSize, int ySize, Bitmap bitmap, RotateFlipType rft, int scale) { #if false System.Console.Out.WriteLine("[TilesOutlineShape.FromBitmap] " + rft.ToString() + ", x" + scale.ToString()); #endif #if false // Note: Some bitmaps are useless (all black or all white) after the RotateFlip() operation! Bitmap template = (Bitmap)bitmap.Clone(); template.RotateFlip(rft); TilesOutlineShape result = new TilesOutlineShape(xSize, ySize, template); #else OutlineShape tile = new ExplicitOutlineShape(bitmap).RotatedOrFlipped(rft); TilesOutlineShape result = new TilesOutlineShape(xSize, ySize, tile as ExplicitOutlineShape); #endif result.SetRepetitions(scale); return(result); }
/// <summary> /// Returns the largest subset of the template shape whose squares are all connected to each other. /// </summary> /// <param name="template"></param> /// <param name="isReserved">defines the maze's reserved areas</param> /// <returns></returns> public static OutlineShape ConnectedSubset(OutlineShape template, InsideShapeDelegate isReserved) { ExplicitOutlineShape result = new ExplicitOutlineShape(template, isReserved); #region Scan the shape for connected areas. byte subsetId = 1; int largestAreaSize = 0; byte largestAreaId = 0; for (int x = 0; x < result.XSize; x++) { for (int y = 0; y < result.YSize; y++) { if (result.squares[x, y] == 1 && subsetId < byte.MaxValue) { int areaSize = result.FillSubset(x, y, ++subsetId); if (areaSize > largestAreaSize) { largestAreaSize = areaSize; largestAreaId = subsetId; } } } } #endregion #region Leave only the largest subset, eliminate all others. for (int x = 0; x < result.XSize; x++) { for (int y = 0; y < result.YSize; y++) { result.SetValue(x, y, (result.squares[x, y] == largestAreaId)); } } #endregion return(result); }
/// <summary> /// Create an outline shape. /// </summary> /// <param name="r">a source of random numbers</param> /// <param name="xSize">width of the created shape</param> /// <param name="ySize">height of the created shape</param> /// <param name="centerX">X coordinate, relative to total width; 0.0 = top, 1.0 = bottom</param> /// <param name="centerY">Y coordinate, relative to total height; 0.0 = left, 1.0 = right</param> /// <param name="shapeSize">size, relative to distance of center from the border; 1.0 will touch the border</param> /// <returns></returns> public static OutlineShape Symbol(Random r, int xSize, int ySize, double centerX, double centerY, double shapeSize) { FontFamily fontFamily = new FontFamily("Times New Roman"); char[] shapeCharacters = { //'\u0040', // @ '\u03C0', // pi '\u05D0', // aleph '\u263B', // smiley '\u2660', // spades '\u2663', // clubs '\u2665', // hearts '\u2666', // diamonds '\u266A', // musical note }; char ch = shapeCharacters[r.Next(shapeCharacters.Length)]; return(ExplicitOutlineShape.Char(xSize, ySize, centerX, centerY, shapeSize, ch, fontFamily)); }
/// <summary> /// Creates a MazeOutlineShape using the given mazeBuilder. /// </summary> /// <param name="xSize"></param> /// <param name="ySize"></param> /// <param name="gridWidth"></param> /// <param name="mazeBuilder"></param> private MazeOutlineShape(int xSize, int ySize, int wallWidth, int gridWidth, MazeShapeBuilder mazeBuilder) : base(xSize, ySize) { this.wallWidth = wallWidth; this.gridWidth = gridWidth; // Determine dimensions of a maze shape that fits tightly around the real maze. int mazeWidth = (XSize - wallWidth + gridWidth - 1) / gridWidth; int mazeHeight = (YSize - wallWidth + gridWidth - 1) / gridWidth; int mazeAreaX = mazeWidth * gridWidth + wallWidth; int mazeAreaY = mazeHeight * gridWidth + wallWidth; this.xOffset = -(mazeAreaX - XSize) / 2; this.yOffset = -(mazeAreaY - YSize) / 2; // Adjust width if the left (and maybe also the right) border would lie on the real maze border. if (wallWidth + xOffset > 0) { mazeWidth += 1; mazeAreaX += gridWidth; xOffset = -(mazeAreaX - XSize) / 2; } if (wallWidth + yOffset > 0) { mazeHeight += 1; mazeAreaY += gridWidth; yOffset = -(mazeAreaY - YSize) / 2; } this.maze = mazeBuilder(mazeWidth, mazeHeight); this.baseShape = new ExplicitOutlineShape(XSize, YSize); PaintBorder(); PaintWalls(); }
/// <summary> /// Returns this shape, augmented by all totally enclosed areas. /// Reserved areas define additional borders around enclosed areas. /// </summary> /// <param name="isReserved">defines the maze's reserved areas</param> /// <returns></returns> public OutlineShape Closure(InsideShapeDelegate isReserved) { return(ExplicitOutlineShape.Closure(this, isReserved)); }
/// <summary> /// Returns this shape, augmented by all totally enclosed areas. /// </summary> /// <returns></returns> public OutlineShape Closure() { return(ExplicitOutlineShape.Closure(this, null)); }
/// <summary> /// Returns the largest subset of this shape whose squares are all connected to each other. /// </summary> /// <returns></returns> public OutlineShape ConnectedSubset(InsideShapeDelegate isReserved) { return(ExplicitOutlineShape.ConnectedSubset(this, isReserved)); }
/// <summary> /// The current GridElement is applied to the given target. /// Every set square is inverted. /// </summary> /// <param name="target"></param> /// <param name="xLeft"></param> /// <param name="yTop"></param> public abstract void Apply(ExplicitOutlineShape target, int xLeft, int yTop);
/// <summary> /// Inverts the target square at the given coordinates. /// </summary> /// <param name="target"></param> /// <param name="x"></param> /// <param name="y"></param> protected void Invert(ExplicitOutlineShape target, int x, int y) { target.SetValue(x, y, !target[x, y]); }
private GridOutlineShape(int xSize, int ySize, GridElement tile) : base(xSize, ySize) { this.baseShape = new ExplicitOutlineShape(xSize, ySize); this.Apply(tile); }
/// <summary> /// Returns the template shape, augmented by all totally enclosed areas. /// </summary> /// <param name="template"></param> /// <param name="isReserved">defines the maze's reserved areas</param> /// <returns></returns> public static OutlineShape Closure(OutlineShape template, InsideShapeDelegate isReserved) { ExplicitOutlineShape result = new ExplicitOutlineShape(template.Inverse()); #region Scan and mark the reserved areas. if (isReserved != null) { byte reservedId = 3; for (int x = 0; x < result.XSize; x++) { for (int y = 0; y < result.YSize; y++) { if (isReserved(x, y)) { result.squares[x, y] = reservedId; } } } } #endregion #region Scan all outside areas. byte outsideId = 2; int x0 = 0, x1 = result.XSize - 1, y0 = 0, y1 = result.YSize - 1; for (int x = 0; x < result.XSize; x++) { if (result.squares[x, y0] == 1) { result.FillSubset(x, y0, outsideId); } if (result.squares[x, y1] == 1) { result.FillSubset(x, y1, outsideId); } } for (int y = 0; y < result.YSize; y++) { if (result.squares[x0, y] == 1) { result.FillSubset(x0, y, outsideId); } if (result.squares[x1, y] == 1) { result.FillSubset(x1, y, outsideId); } } #endregion #region Add the areas which were not reached. for (int x = 0; x < result.XSize; x++) { for (int y = 0; y < result.YSize; y++) { // 0: square is part of the template (not part of its inverse) // 1: square is not part of the template, but was not reached result.SetValue(x, y, (result.squares[x, y] <= 1)); } } #endregion return(result); }