/// <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> /// 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> /// 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]); }
/// <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); }