예제 #1
0
파일: Room.cs 프로젝트: kraftwerk15/RoomKit
 /// <summary>
 /// Creates and sets a rectangular Room Perimeter with dimensions derived from Room characteristics with its southwest corner at the origin or at the 2D location specified by the supplied Vector3.
 /// </summary>
 /// <returns>
 /// True if the Perimeter is successfully set.
 /// </returns>
 public bool SetPerimeter(Vector3 moveTo = null, double width = 0.0)
 {
     if (width > 0.0)
     {
         if (DesignSet)
         {
             Perimeter = Shaper.Rectangle(DesignLength * DesignWidth / width, width, moveTo);
             return(true);
         }
         else if (DesignArea > 0.0)
         {
             Perimeter = Shaper.Rectangle(DesignArea / width, width, moveTo);
             return(true);
         }
     }
     else if (DesignSet)
     {
         Perimeter = Shaper.Rectangle(DesignLength, DesignWidth, moveTo);
         return(true);
     }
     else if (DesignArea > 0.0 && DesignRatio > 0.0)
     {
         Perimeter = Shaper.RectangleByArea(DesignArea, DesignRatio, moveTo);
         return(true);
     }
     return(false);
 }
예제 #2
0
        /// <summary>
        /// Creates or expands a final Room in the remaining RoomRow area.
        /// </summary>
        /// <param name="height">Desired height of the Room if a new Room must be created.</param>
        /// <param name="tolerance">Minimum area of a infill Room. Smaller rooms will be joined to the last Room.</param>
        public void Infill(double height, bool join = false)
        {
            height = Math.Round(Math.Abs(height));
            if (height.NearEqual(0.0) || insert.IsAlmostEqualTo(compass.SE))
            {
                return;
            }
            var polygons = Shaper.Intersections(Polygon.Rectangle(insert, compass.NE).ToList(), perimeterJig.ToList());

            if (polygons.Count == 0)
            {
                return;
            }
            if (polygons.Count > 0 && Rooms.Count > 0 && (join || polygons.First().Area() < Tolerance))
            {
                Rooms.Last().Perimeter =
                    Shaper.Merge(
                        new List <Polygon>
                {
                    polygons.First().Rotate(Vector3.Origin, Angle),
                    Rooms.Last().Perimeter
                }).First();
                return;
            }
            if (Rooms.Count > 0)
            {
                Rooms.Add(new Room(polygons.First().Rotate(Vector3.Origin, Angle), height));
                return;
            }
            Rooms.Add(new Room(perimeterJig.Rotate(Vector3.Origin, Angle), height));
            insert = compass.SE;
        }
예제 #3
0
        /// <summary>
        /// Attempts to place a Room perimeter on the next open segment of the row.
        /// </summary>
        /// <param name="room">Room from which to derive the Polygon to place.</param>
        /// <returns>
        /// True if the room was successfully placed.
        /// </returns>
        public bool AddRoom(Room room, bool within = true)
        {
            if (room == null ||
                insert.DistanceTo(compass.SE) < 1.0 ||
                compass.SW.DistanceTo(insert) >= compass.SW.DistanceTo(compass.SE))
            {
                return(false);
            }
            var     ratio  = room.DesignRatio > 1.0 ? 1 / room.DesignRatio : room.DesignRatio;
            var     length = Math.Sqrt(room.Area * ratio);
            Polygon polygon;

            if (within)
            {
                var polygons = Shaper.Intersections(
                    Polygon.Rectangle(insert, new Vector3(insert.X + length, insert.Y + compass.SizeY)).ToList(),
                    perimeterJig.ToList());
                if (polygons.Count == 0)
                {
                    return(false);
                }
                polygon = polygons.First();
            }
            else
            {
                polygon = Polygon.Rectangle(insert, new Vector3(insert.X + length, insert.Y + compass.SizeY));
            }
            insert         = polygon.Compass().SE;
            room.Perimeter = polygon.Rotate(Vector3.Origin, Angle);
            room.Placed    = true;
            Rooms.Add(room);
            return(true);
        }
예제 #4
0
 /// <summary>
 /// Creates and sets a rectangular Room Perimeter with dimensions derived from Room characteristics with its southwest corner at the supplied Vector3 point. If no point is supplied, the southwest corner is placed at the origin.
 /// </summary>
 /// <param name="area">Area override for the new Room Perimeter. If zero, defaults to the value of DesignArea.</param>
 /// <param name="ratio">Desired ratio of X to Y Room dimensions.</param>
 /// <param name="moveTo">Vector3 location of the new Polygon's southwest corner.</param>
 /// <returns>
 /// True if the Perimeter is successfully set.
 /// </returns>
 public bool SetPerimeter(double area, double ratio = 1.5, Vector3 moveTo = null)
 {
     if (area <= 0.0 || ratio <= 0.0)
     {
         return(false);
     }
     Perimeter = Shaper.PolygonByArea(area, ratio, moveTo);
     return(true);
 }
예제 #5
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="suite"></param>
        /// <returns></returns>
        private void PlaceByAxis()
        {
            if (Rooms.Count == 0)
            {
                return;
            }
            var area = 0.0;

            foreach (var room in Rooms)
            {
                area += room.Area;
            }
            var roomRows = new List <RoomRow>();

            while (RoomsPlaced.Count < Rooms.Count)
            {
                roomRows.Clear();
                Perimeter = Shaper.RectangleByArea(area * 0.5, Ratio);
                var roomRow = new RoomRow(Perimeter);
                roomRows.Add(roomRow);
                var i = 0;
                while (i < Rooms.Count)
                {
                    if (!roomRow.AddRoomFitted(Rooms[i], false))
                    {
                        break;
                    }
                    i++;
                }
                if (i == Rooms.Count)
                {
                    return;
                }
                var compass  = Perimeter.Compass();
                var row      = new Line(compass.SE, compass.SW);
                var matchRow = new RoomRow(row, area / row.Length());
                roomRows.Add(matchRow);
                while (i < Rooms.Count)
                {
                    if (!matchRow.AddRoomFitted(Rooms[i], false))
                    {
                        break;
                    }
                    i++;
                }
                Ratio += 0.1;
            }
            Rooms.Clear();
            foreach (var roomRow in roomRows)
            {
                foreach (var room in roomRow.Rooms)
                {
                    Rooms.Add(room);
                }
            }
            Perimeter = Footprint;
        }
예제 #6
0
파일: Room.cs 프로젝트: kraftwerk15/RoomKit
 /// <summary>
 /// Creates and sets a rectangular Room Perimeter, Height, and southwest corner location with a supplied vectors.
 /// Sets the DesignX and DesignY properties.
 /// </summary>
 /// <param name="xyz">Vector3 dimensions of a new Polygon Perimeter. If xy.Z is > 0.0, sets the height of the Room.</param>
 /// <param name="moveTo">Vector3 location of the new Polygon's southwest corner.</param>
 /// <returns>
 /// True if the Perimeter is successfully set.
 /// </returns>
 public bool SetDimensions(Vector3 xyz, Vector3 moveTo = null)
 {
     if (xyz.X <= 0.0 || xyz.Y <= 0.0)
     {
         return(false);
     }
     perimeter = Shaper.Rectangle(xyz.X, xyz.Y, moveTo);
     height    = xyz.Z > 0.0 ? xyz.Z : height;
     return(true);
 }
예제 #7
0
 /// <summary>
 /// Creates and sets a rectangular Room Perimeter, Height, and southwest corner location with a supplied vectors.
 /// Sets the DesignX and DesignY properties.
 /// </summary>
 /// <param name="xyz">Vector3 dimensions of a new Polygon Perimeter. If xy.Z is > 0.0, sets the height of the Room.</param>
 /// <param name="moveTo">Vector3 location of the new Polygon's southwest corner.</param>
 /// <returns>
 /// True if the Perimeter is successfully set.
 /// </returns>
 public bool SetDimensions(Vector3 xyz, Vector3 moveTo = null)
 {
     if (xyz.X <= 0.0 || xyz.Y <= 0.0)
     {
         return(false);
     }
     Perimeter = Shaper.PolygonBox(xyz.X, xyz.Y, moveTo);
     if (xyz.Z > 0.0)
     {
         Height = xyz.Z;
     }
     return(true);
 }
예제 #8
0
        /// <summary>
        /// Adds a Room to the Corridors list.
        /// </summary>
        /// <param name="room">Room to add.</param>
        /// <param name="fit">Indicates whether the new corridor should mutually fit to other Story features.</param>
        /// <param name="merge">Indicates whether the new corridor should merge with other corridors.</param>
        /// <returns>
        /// True if one or more rooms were added to the Story.
        /// </returns>
        public bool AddCorridor(Room room, bool fit = true, bool merge = true)
        {
            if (Perimeter == null || room.Perimeter == null || !Perimeter.Covers(perimeter))
            {
                return(false);
            }
            var newRoom =
                new Room()
            {
                Color     = room.Color,
                Elevation = Elevation,
                Height    = room.Height,
                Name      = room.Name,
                Perimeter = room.Perimeter
            };
            var fitRooms = new List <Room> {
                newRoom
            };

            if (fit)
            {
                var toRooms = new List <Room>(Exclusions);
                toRooms.AddRange(Services);
                fitRooms = FitRooms(fitRooms, toRooms, false);
                Rooms    = FitRooms(Rooms, fitRooms);
            }
            if (fitRooms.Count == 0)
            {
                return(false);
            }
            Corridors.AddRange(fitRooms);
            var merged = Shaper.Merge(CorridorsAsPolygons);

            if (merge && merged.Count < Corridors.Count)
            {
                Corridors.Clear();
                foreach (var corridor in merged)
                {
                    Corridors.Add(
                        new Room()
                    {
                        Color     = room.Color,
                        Elevation = Elevation,
                        Height    = room.Height,
                        Name      = room.Name,
                        Perimeter = corridor
                    });
                }
            }
            return(true);
        }
예제 #9
0
 /// <summary>
 /// Creates and sets a rectangular Room Perimeter with dimensions derived from Room characteristics with its southwest corner at the origin or at the 2D location implied by the supplied Vector3.
 /// </summary>
 /// <returns>
 /// True if the Perimeter is successfully set.
 /// </returns>
 public bool SetPerimeter(Vector3 moveTo = null)
 {
     if (DesignSet)
     {
         Perimeter = Shaper.PolygonBox(DesignLength, DesignWidth, moveTo);
         return(true);
     }
     else if (DesignArea > 0.0 && DesignRatio > 0.0)
     {
         Perimeter = Shaper.PolygonByArea(DesignArea, DesignRatio, moveTo);
         return(true);
     }
     return(false);
 }
예제 #10
0
파일: Room.cs 프로젝트: vdubya/RoomKit
 /// <summary>
 /// Creates a Polygon perimeter at the origin with dimensions derived from Room characteristics. Assumes the Perimeter will be relocated and so omits setting the Room's Perimeter.
 /// </summary>
 /// <returns>
 /// A new rectilinear Polygon derived either from fixed dimensions or as a rectilinear target area of a randomly determined ratio between 1 and 2 between the Room's X and Y dimensions.
 /// </returns>
 public Polygon MakePerimeter()
 {
     if (Perimeter != null)
     {
         return(Perimeter);
     }
     if (DesignX > 0.0 && DesignY > 0.0)
     {
         return(Shaper.PolygonBox(DesignX, DesignY));
     }
     else
     {
         return(Shaper.AreaFromCorner(DesignArea, Shaper.RandomDouble(1, 2)));
     }
 }
예제 #11
0
파일: RoomGroup.cs 프로젝트: vdubya/RoomKit
 /// <summary>
 /// Creates a Polygon perimeter derived from the supplied Room characteristics.
 /// </summary>
 /// <param name="room">The Room from which to derive the Polygon to place.</param>
 /// <returns>
 /// A new Polygon derived either from fixed dimensions or as a variably proportioned area.
 /// </returns>
 private Polygon RoomPerimeter(Room room)
 {
     if (room.Perimeter != null)
     {
         return(room.Perimeter);
     }
     if (room.DesignX > 0.0 && room.DesignY > 0.0)
     {
         return(Shaper.PolygonBox(room.DesignX, room.DesignY));
     }
     else
     {
         return(Shaper.AreaFromCorner(room.DesignArea, Shaper.RandomDouble(1, 2)));
     }
 }
예제 #12
0
파일: Room.cs 프로젝트: vdubya/RoomKit
        /// <summary>
        /// Places a Polygon east of another Polygon, attempting to align bounding box corners or the horizontal bounding box axis.
        /// </summary>
        /// <param name="polygon">The Polygon to be placed adjacent to another Polygon.</param>
        /// <param name="adjTo">The Polygon adjacent to which the new Polygon will be located.</param>
        /// <param name="perimeter">The Polygon that must cover the resulting Polygon.</param>
        /// <param name="among">The collection of Polygons that must not intersect the resulting Polygon.</param>
        /// <returns>
        ///  A new Polygon or null if the conditions of placement cannot be satisfied.
        /// </returns>



        /// <summary>
        /// Creates and sets a rectangular Room Perimeter with dimensions derived from Room characteristics with its southwest corner at the supplied Vector3 point. If no point is supplied, the southwest corner is placed at the origin.
        /// </summary>
        /// <param name="moveTo">The Vector3 indication the location of new Polygon's southwest corner.</param>
        /// <returns>
        /// A new rectilinear Polygon derived either from fixed DesignX and DesignY dimensions or as a rectilinear target area of a random ratio between 1 and 2 of the Room's X to Y dimensions.
        /// </returns>
        public Polygon MakePerimeter(Vector3 moveTo = null)
        {
            if (DesignX > 0.0 && DesignY > 0.0)
            {
                Perimeter = Shaper.PolygonBox(DesignX, DesignY);
            }
            else
            {
                Perimeter = Shaper.AreaFromCorner(DesignArea, Shaper.RandomDouble(1, 2));
            }
            if (moveTo != null)
            {
                Perimeter = Perimeter.MoveFromTo(new Vector3(), moveTo);
            }
            return(Perimeter);
        }
예제 #13
0
 /// <summary>
 /// Constructor creates a Room by a positive area and a positive x : y dimension ratio and a positive height with the SW corner at the origin.
 /// </summary>
 public Room(double area, double ratio, double height)
 {
     area        = area.NearEqual(0.0) ? 1.0 : Math.Round(Math.Abs(area), PRECISION);
     ratio       = ratio.NearEqual(0.0) ? 1.0 : Math.Round(Math.Abs(ratio), PRECISION);
     height      = height.NearEqual(0.0) ? 1.0 : Math.Round(Math.Abs(height), PRECISION);
     Color       = Palette.White;
     Department  = "";
     DesignArea  = area;
     DesignRatio = ratio;
     Height      = height;
     Name        = "";
     Number      = "";
     Perimeter   = Shaper.RectangleByArea(area, ratio);
     elevation   = 0.0;
     Placed      = false;
     Suite       = "";
     SuiteID     = "";
     UniqueID    = Guid.NewGuid().ToString();
 }
예제 #14
0
        /// <summary>
        /// Tests if the supplied Polygon resides in a corner of a Polygon perimeter.
        /// </summary>
        /// <param name="polygon">The Polygon to test.</param>
        /// <param name="perimeter">The Polygon to test against.</param>
        /// <returns>
        /// Returns true if exactly three of the polygon bounding box points fall on the Polygon perimeter bounding box.
        /// </returns>
        public static bool AtCorner(this Polygon polygon, Polygon perimeter)
        {
            var count    = 0;
            var box      = new TopoBox(perimeter);
            var boundary = Shaper.PolygonBox(box.SizeX, box.SizeY).MoveFromTo(Vector3.Origin, box.SW);

            foreach (Vector3 vertex in BoxCorners(polygon))
            {
                if (boundary.Touches(vertex))
                {
                    count++;
                }
            }
            if (count != 3)
            {
                return(false);
            }
            return(true);
        }
예제 #15
0
        /// <summary>
        /// Attempts to place a Room perimeter on the next open segment of the row.
        /// </summary>
        /// <param name="room">Room from which to derive the Polygon to place.</param>
        /// <returns>
        /// True if the Room was successfully placed.
        /// </returns>
        public bool AddRoomFitted(Room room, bool within = true)
        {
            if (room == null ||
                insert.DistanceTo(compass.SE) < Tolerance ||
                compass.SW.DistanceTo(insert).NearEqual(compass.SW.DistanceTo(compass.SE)))
            {
                return(false);
            }
            var ratio    = room.DesignRatio > 1.0 ? 1 / room.DesignRatio : room.DesignRatio;
            var boundary = within ? new Polygon(perimeterJig.Vertices) : null;
            var polygon  =
                Shaper.RectangleByRatio(ratio).MoveFromTo(Vector3.Origin, insert)
                .ExpandToArea(room.DesignArea, ratio, Tolerance, Orient.SW, boundary, RoomsAsPolygons);

            insert         = polygon.Compass().SE;
            room.Perimeter = polygon.Rotate(Vector3.Origin, Angle);
            room.Placed    = true;
            Rooms.Add(room);
            return(true);
        }
예제 #16
0
        /// <summary>
        /// Adds a Room to the Corridors list.
        /// </summary>
        /// <param name="room">Room to add.</param>
        /// <returns>
        /// True if one or more Rooms were added.
        /// </returns>
        public bool AddCorridor(Room room,
                                bool merge       = true,
                                double tolerance = 0.0,
                                double minLength = 0.0)
        {
            var perimeters = Shaper.FitWithin(room.Perimeter, Perimeter);

            if (perimeters.Count == 0)
            {
                return(false);
            }
            var fitAmong = new List <Polygon>(OpeningsAsPolygons);

            fitAmong.AddRange(ExclusionsAsPolygons);
            fitAmong.AddRange(ServicesAsPolygons);
            var corridors = Shaper.Differences(perimeters, fitAmong).ToList();

            if (corridors.Count() == 0)
            {
                return(false);
            }
            corridors.AddRange(CorridorsAsPolygons);
            if (merge)
            {
                corridors = Shaper.Merge(corridors);
            }
            Corridors.Clear();
            foreach (var corridor in corridors)
            {
                Corridors.Add(
                    new Room(room)
                {
                    Elevation = Elevation,
                    Height    = room.Height,
                    Perimeter = corridor
                });
            }
            FitRooms();
            return(true);
        }
예제 #17
0
        /// <summary>
        /// Private function conforming a list of Rooms to another list of Rooms.
        /// </summary>
        /// <param name="fitRooms">Rooms that will conform the 'to' list of Rooms.</param>
        /// <param name="toRooms">List of Rooms to which the 'fitted' Rooms will conform.</param>
        /// <param name="toStory">Indicates whether the new Room should conform to the Story Perimeter. True by default.</param>
        /// <returns>
        /// A list of Rooms.
        /// </returns>
        private List <Room> FitRooms(List <Room> fitRooms, List <Room> toRooms, bool toStory = true)
        {
            if (toRooms.Count == 0)
            {
                return(fitRooms);
            }
            var addRooms   = new List <Room>();
            var toPolygons = new List <Polygon>();

            foreach (Room toRoom in toRooms)
            {
                toPolygons.Add(toRoom.Perimeter);
            }
            Polygon storyPerimeter = Perimeter;

            if (!toStory)
            {
                storyPerimeter = null;
            }
            foreach (Room fitRoom in fitRooms)
            {
                var perimeters = Shaper.FitTo(fitRoom.Perimeter, storyPerimeter, toPolygons);
                for (int i = 0; i < perimeters.Count; i++)
                {
                    var addRoom = new Room()
                    {
                        Color     = fitRoom.Color,
                        Elevation = fitRoom.Elevation,
                        Height    = fitRoom.Height,
                        Name      = fitRoom.Name,
                        Perimeter = perimeters[i]
                    };
                    addRooms.Add(addRoom);
                }
            }
            return(addRooms);
        }
예제 #18
0
        /// <summary>
        /// Clears the current Rooms list and creates new Rooms defined by orthogonal x- and y-axis divisions of the RoomGroup Perimeter.
        /// </summary>
        /// <param name="xRooms">The quantity of Rooms along orthogonal x-axis. Must be positive.</param>
        /// <param name="yRooms">The quantity of Rooms along orthogonal y-axis. Must be positive.</param>
        /// <returns>
        /// True if the Rooms are created.
        /// </returns>
        public bool RoomsByDivision(int xRooms = 1, int yRooms = 1, double height = 3.0, string name = "")
        {
            if (Perimeter == null || xRooms < 1 || yRooms < 1 || height <= 0.0)
            {
                return(false);
            }
            var sizeX    = SizeX / xRooms;
            var sizeY    = SizeY / yRooms;
            var count    = xRooms * yRooms;
            var box      = new TopoBox(Perimeter);
            var newRooms = new List <Room>();

            for (int xIdx = 0; xIdx < xRooms; xIdx++)
            {
                var xCoord = box.SW.X + (xIdx * sizeX);
                for (int yIdx = 0; yIdx < yRooms; yIdx++)
                {
                    var yCoord  = box.SW.Y + (yIdx * sizeY);
                    var polygon = Shaper.Rectangle(sizeX, sizeY);
                    polygon = polygon.MoveFromTo(Vector3.Origin, new Vector3(xCoord, yCoord)).Intersection(Perimeter).First();
                    var room = new Room()
                    {
                        Height    = height,
                        Name      = name,
                        Perimeter = polygon
                    };
                    newRooms.Add(room);
                }
            }
            if (newRooms.Count == 0)
            {
                return(false);
            }
            Rooms.Clear();
            Rooms.AddRange(newRooms);
            return(true);
        }
예제 #19
0
 /// <summary>
 /// Joins triangular end Rooms to the adjacent Room.
 /// </summary>
 /// <returns>An integer new Room count.</returns>
 public int JoinTriangleEndRooms()
 {
     for (var i = 0; i < 2; i++)
     {
         if (Rooms.Count < 2)
         {
             return(Rooms.Count);
         }
         var perimeter = Rooms[0].Perimeter;
         if (perimeter.Vertices.Count == 3)
         {
             var perimeters = Shaper.Merge(new List <Polygon> {
                 perimeter, Rooms[1].Perimeter
             });
             if (perimeters.Count == 1)
             {
                 Rooms[1].Perimeter = perimeters[0];
                 Rooms = Rooms.Skip(1).ToList();
             }
         }
         Rooms.Reverse();
     }
     return(Rooms.Count);
 }
예제 #20
0
 /// <summary>
 /// Joins end Rooms of an area less than the supplied area to the adjacent Room.
 /// </summary>
 /// <param name="minArea">Minimum area of a ditinct Room.</param>
 /// <returns>An integer new Room count.</returns>
 public int JoinSmallEndRooms(double minArea = 0.1)
 {
     for (var i = 0; i < 2; i++)
     {
         if (Rooms.Count < 2)
         {
             return(Rooms.Count);
         }
         var perimeter = Rooms[0].Perimeter;
         if (perimeter.Area() < minArea)
         {
             var perimeters = Shaper.Merge(new List <Polygon> {
                 perimeter, Rooms[1].Perimeter
             });
             if (perimeters.Count == 1)
             {
                 Rooms[1].Perimeter = perimeters[0];
                 Rooms = Rooms.Skip(1).ToList();
             }
         }
         Rooms.Reverse();
     }
     return(Rooms.Count);
 }
예제 #21
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="suite"></param>
        /// <returns></returns>
        private void PlaceReciprocal()
        {
            if (Rooms.Count == 0)
            {
                return;
            }
            var area = 0.0;

            foreach (var room in Rooms)
            {
                area += room.Area;
            }
            var roomRows = new List <RoomRow>();

            while (RoomsPlaced.Count < Rooms.Count)
            {
                roomRows.Clear();
                Perimeter = Shaper.RectangleByArea(area, Ratio);
                var roomRow = new RoomRow(Perimeter);
                roomRows.Add(roomRow);
                var i = 0;
                while (i < Rooms.Count)
                {
                    if (!roomRow.AddRoomFitted(Rooms[i], false))
                    {
                        break;
                    }
                    i++;
                }
                if (i == Rooms.Count)
                {
                    return;
                }
                var perimeter = Shaper.Differences(roomRow.Perimeter.ToList(), roomRow.Footprint.ToList());
                if (perimeter.Count == 0)
                {
                    break;
                }
                var matchRow = new RoomRow(perimeter.First());
                roomRows.Add(matchRow);
                while (i < Rooms.Count)
                {
                    if (!matchRow.AddRoomFitted(Rooms[i], true))
                    {
                        break;
                    }
                    i++;
                }
                Ratio += 0.1;
            }
            Rooms.Clear();
            foreach (var roomRow in roomRows)
            {
                foreach (var room in roomRow.Rooms)
                {
                    Rooms.Add(room);
                }
            }
            //2020.07.09 Anthony Hauck
            //Commented code makes suite geometric room diffs into one or more extra rooms.
            //Overall the results were more confusing that just ignoring them, but leaving
            //this here in case future developers have a better idea.
            //var lastRow = roomRows.Last();
            //var rooms = lastRow.RoomsAsPolygons;
            //var diffs = lastRow.Perimeter.Difference(rooms).ToList();
            //if (diffs.Count > 0)
            //{
            //    var height = lastRow.Rooms.Last().Height;
            //    diffs = Shaper.Merge(diffs);
            //    foreach (var diff in diffs)
            //    {
            //        Rooms.Add(
            //            new Room(diff, height)
            //            {
            //                Color = Palette.White
            //            });
            //    }
            //}
            Perimeter = Footprint;
        }