Creating a floor plan generator to help with moving furniture
Recently, my husband and I decided to swap rooms for our office and bed. Over the years we've been in this apartment, the layout just wasn't working for us. We needed more space for our office to be comfortable and there was no way we could cram more desk into the existing office. Our bedroom was oddly shaped. It's huge, but long - and our bed never quite fit in without leaving a tiny amount of space between the bed and TV stand.
In the past, I had modified floor plans to make sure all of our furniture would fit where we wanted by taking measurements and drawing out the floor plan in Adobe Fireworks. I know, it's ancient and hasn't been updated in many years now. But I stuck with it because I just loved the super simplicity of using it. More recently, I've tried using Gravit Designer as a simple replacement. But even that program sometimes makes it feel a bit too complicated to do very basic things and I found myself frustrated trying to recreate what I had done in Fireworks.
So why not build yet another thing into my Intranet? Given this is my first blog about it, you probably aren't aware of all the things that I've built into my Intranet in the past. But I have a habit of just building highly customized modules that serve my needs. In this case, it turned into a very simple application for entering the lengths of walls, positions of doors and windows, and dimensions and positions of objects within each room. Here's a screenshot of the overall result of what I did.
The entirety of a floor plan is drawn out using various elements inside an <svg>
rather than using a canvas. I considered using a canvas at the beginning, but I have more experience manipulating SVGs, and utilizing a canvas seemed a bit like overkill for the simplistic nature of what I was trying to do. I wasn't interested in rebuilding a graphics editor here. Most of the data I had to be entered was already in the format of numbers representing the lengths of walls. If I was going to be typing in a bunch of numbers anyways, having cool drag-and-drop functionalities seemed unnecessary and a waste of time.
Floor plans
I can create new floor plans using the Plus button at the top of the list, which will add new options to the dropdown. Whichever one is currently set as the primary residence is auto-loaded with the page. But I figured, at some point when we decide to move in the future, we're going to want to be able to create multiple floor plans for the options we have available and see how things fit into them. While there's only one floor plan now, it wasn't hard to build the system to handle many now and save me some hassle of reworking everything in the future.
Rooms
For each floor plan, I can add any number of rooms. Each room can have its own color assigned and also resets the coordinates for (0, 0) for the frames and objects (further explained below) to make it easier to position things within each room once the walls have been specified.
Walls
The essential part of each room is specifying the walls. Because not all rooms are perfect rectangles (as can be seen from the screenshot), I couldn't just set each room to take in a width and height with coordinates. Instead, each wall is specified as an x and y modifier. If I want the wall to extend to the right without moving up or down, I simply specify a positive x value. Making it move down, I specify a positive y value. As an example, what is now the office portion of our apartment (the red room in the screenshot) is specified by the following values:
x | y |
---|---|
755 | 0 |
0 | 312 |
-371 | 0 |
0 | -36 |
-384 | 0 |
With the walls entered, my code can loop through them all to convert them into a points
attribute of a <polygon>
to draw the box that will be the background behind frames and objects of the room. It also records the highest coordinates to keep track of the overall width and height of the room.
var x = this.xStart + 24, y = this.yStart + 24, points = `${x},${y}`;
this.walls.forEach(function (wall) {
x += wall.xLength;
y += wall.yLength;
if (x - this.xStart + 24 > this.width) this.width = x - this.xStart + 24;
if (y - this.yStart + 24 > this.height) this.height = y - this.yStart + 24;
points += ` ${x},${y}`;
}.bind(this));
Frames
Knowing where all the doors and windows are inside a room is also important so I don't place things in front of them that shouldn't be. The frame sections lets me specify doors and windows, along with an angle to represent how far and which direction they open (by drawing the extra arc onto the SVG). The interface for entering a frame looks very similar to that of the objects so I won't go over it much.
Objects
Finally, I can add objects into the room. This is the part of the process visible in the screenshot, where I can specify coordinates, dimensions, rotation, and roundness of the object (to help differentiate objects that don't have sharp edges). Behind the scenes, I built the objects tables to allow for two types of objects: standard objects using all the files just mentioned and custom objects which were manually specified coordinates that could be dropped directly into a <path>
element. This would allow for adding more complicated objects into the room, such as a monitor, without being forced to use standard rectangles. However, I never implemented the custom object UI because I just didn't need it at the time. That will be an extra project I complete in the future.
The objects section also has an extra button that allows for duplicating an existing object. With the copy created, I can quickly modify the coordinates to have another of the same thing without having to manually enter all the extra details again.