/// <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);
        }
Example #3
0
 /// <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);
        }