public FilledRegion Apply(FillData data) { //Get the space between each shell. In most cases it should be 1. //In the case of a very large room, it could be 2. //Played around with a graphing calculator to get a good function that reflects this. int minSpace = 1, maxSpace = 2; double coefficient = Math.Sqrt(maxSpace - minSpace) / (data.BeingFilled.Area - MinArea); int spaceBetweenShells = (int)Math.Round(Math.Pow(coefficient * (data.BeingFilled.Area - MinArea), 2.0) + 1.0, 0); //Make sure my function is valid. if (spaceBetweenShells < 1 || spaceBetweenShells > 2) { throw new InvalidOperationException("Oops!"); } //Continuously fill in smaller and smaller shells centered around the region center. Region shell = new Region(data.BeingFilled.TopLeft.Right.Below, data.BeingFilled.BottomRight.Left.Above, true); sbyte holeDir = 1; //Smallest-allowable shell is 3x3, which means a region of width/height 2. while (shell.Area > 4) { //Fill in the perimeter. data.FillPerimeter(true, shell); //Clear the hole. if (holeDir < 0) { data.SetMapAt(shell.LeftMid, false); } else { data.SetMapAt(shell.RightMid, false); } //Flip the side the next hole will be on. holeDir *= -1; //Shrink the shell. for (int i = 0; i < spaceBetweenShells; ++i) { shell = new Region(shell.TopLeft.Right.Below, shell.BottomRight.Left.Above, false); } } return(new ConcentricSquaresRegion(data.BeingFilled, new Region(shell.TopLeft.Left.Above, shell.BottomRight.Right.Below))); }
public FilledRegion Apply(FillData data) { Region clearArea = new Region(data.BeingFilled.Center.Left.Above, data.BeingFilled.Center.Right.Below); //Fill the inner ring. //Move from each corner of the X to the center. Location counter; Location center = data.BeingFilled.Center; bool moveHorizontally; bool horizontalBias = data.BeingFilled.Width > data.BeingFilled.Height; //Fill in the center. data.FillRegion(true, new Region(clearArea.TopLeft.Above.Left, clearArea.BottomRight.Below.Right)); for (int i = 0; i < 4; ++i) { //Get the point to start from. switch (i) { case 0: counter = data.BeingFilled.TopLeft.Right.Below; data.SetMapAt(counter, false); break; case 1: counter = data.BeingFilled.TopRight.Left.Below; data.SetMapAt(counter, false); break; case 2: counter = data.BeingFilled.BottomLeft.Right.Above; data.SetMapAt(counter, false); break; case 3: counter = data.BeingFilled.BottomRight.Left.Above; data.SetMapAt(counter, false); break; default: throw new InvalidOperationException(); } //Clear a path to the center. while (!clearArea.Touches(counter, true, true, true)) { //Get which direction to move in. float relativeDistX = Math.Abs(counter.X - center.X) / (float)data.BeingFilled.Width; float relativeDistY = Math.Abs(counter.Y - center.Y) / (float)data.BeingFilled.Height; if (relativeDistX > relativeDistY) { moveHorizontally = true; } else if (relativeDistX < relativeDistY) { moveHorizontally = false; } else { moveHorizontally = horizontalBias; } //Move that direction, add the region, and fill part of the X after moving. if (moveHorizontally) { int amount = Math.Sign(center.X - counter.X); counter.X += amount; data.SetMapAt(counter, false); data.SetMapAt(new Location(counter.X + amount, counter.Y), true); if (counter.Y > 1 && counter.Y < data.BeingFilled.Bottom - 1) { data.SetMapAt(new Location(counter.X, counter.Y + Math.Sign(counter.Y - center.Y)), true); } } else { int amount = Math.Sign(center.Y - counter.Y); counter.Y += amount; data.SetMapAt(counter, false); data.SetMapAt(new Location(counter.X, counter.Y + amount), true); if (counter.X > 1 && counter.X < data.BeingFilled.Right - 1) { data.SetMapAt(new Location(counter.X + Math.Sign(counter.X - center.X), counter.Y), true); } } } //Keep the center clear. data.FillRegion(false, clearArea); } //Just to be safe, clear the perimeter at the end. data.FillPerimeter(false, data.BeingFilled); return(new XRegion(data.BeingFilled, data.Map)); }