Two impromptus--or how Python helped us design our kitchen

Andrew Koenig
Gillette, New Jersey

Abstract

My wife and I recently remodeled our house extensively, including the kitchen. Among the kitchen-remodeling decisions was the shape of part of the counter and the design of the tile wall behind the stove. I wrote small Python programs to help with these two parts of the design.

This note recounts some of my thoughts in writing these programs, and discusses some of the attributes of Python that led me to choose it for this purpose.

The problems

When my wife and I remodeled our kitchen, one of our goals was to change the shape of our kitchen counter to provide better seating. The old counter was made of plastic laminate over plywood, and we had specified its shape so as to make it easy to fabricate: as a sequence of straight segments and circular arcs. Part of the counter extended past the end of the kitchen proper to form an eating area that went part way into the living room. The size and location of that extension worked well to entertain guests, but the somewhat abrupt corners and limited overhang made it less comfortable than it might be for eating.

Accordingly, when we started planning to remodel the kitchen, we thought about a more appropriate shape for that part of the counter. This counter was to be made of synthetic stone, and we believed that the company that was to fabricate the counter could make it in any shape we wanted--provided that we could specify it accurately enough.

I remembered reading, a number of years ago (Martin Gardner: The "Superellipse," a curve that lies between the ellipse and the rectangle, Scientific American, September 1965), that the Danish architect Piet Hein had come up with a family of shapes that combined some nice properties of circles and rectangles. I felt that one of these shapes might be just what we needed for the counter: corners that were gently rounded enough that people could sit at them, but more space than a circular extension would provide.

I remembered the fundamental equation for what I thought would be the right curve: x2.5+y2.5=r2.5, where r is the (minimum) radius of the desired shape. However, that memory left me with three problems:

• Would such a curve work as well in practice as it did in theory?
• How would the eating area transition to the rest of the kitchen counter?
• How could we specify the shape for fabrication?

Another uncertainty in the kitchen design was the tile wall behind the stove. We knew the shape of that area, and we had picked out a collection of tile colors that we liked. However, the tile store had boxes of tiles in some of those colors that had already been opened. If we needed any more tile than was in each of those boxes, we would have had to buy an additional full box, which was much more tile than we needed for this small area. Accordingly, we had to come up with a tile design that we liked, and that also did not exhaust the available tile.

The remainder of this paper will discuss the Python programs that I wrote to help solve these problems.

Shaping the counter

It is, of course, easy to write a program in any sensible programming language that will compute the coordinates of a series of points according to a given formula. The difficult part is finding the right way to deal with those coordinates. I wanted

• to be able to experiment with several formulas to be sure of which curve we really wanted wanted.
• to try various dimensions to see how they would fit with the rest of the countertop.
• to produce small-scale drawings for planning purposes, and then to produce accurate, life-sized drawings of the final design for the counter fabricators.
This last requirement was the most difficult, as I did not have access to a printer that could produce sufficiently large output. My only recourse was somehow to clip sections out of the final image that I could print on my 8½-by-11-inch laser printer.

In other words, I expected the hard part of the problem to be producing first a small-scale drawing, and then clipping appropriate sections out of a full-scale version of the same drawing so that I could print them and tape the sheets together. To simplify the problem, I started by looking around for software that I already knew how to use, and that might help with that part of the problem.

After a few false starts, I realized that I had actually been using such a program for many years: The `dpost` program that translates `troff` output into PostScript form has options to specify magnification, along with the x and y coordinates of the area to be magnified. So what I needed was a way to translate the coordinates that I would generate into `troff` form.

Fortunately, `troff` has a companion program named `pic` that makes it easy to deal with coordinates. I could therefore reduce the problem to one of transforming the coordinates--once I had computed them--into appropriate `pic` incantations. That realization made it clear that the programming I had to do was fairly straightforward numerical computation and text manipulation--a nearly ideal match for Python.

I know C++ much better than I know Python, and have been using it for much longer. Moreover, there is nothing in this program that makes it particularly ill-suited for C++. Why, then, did I do it in Python? The main reason was faster turnaround: Because of Python's interpretive implementation, I could see the effect of a change within a second or two, compared with 10-20 seconds in C++. Moreover, I found it hard to imagine that the final program would take long to execute, so run-time performance wasn't a factor.

The program itself is fairly straightforward, with no particular efforts toward elegance or long-term maintainability. Nevertheless, it has a few points of interest:

• Because we knew the countertop would be symmetric, the program generates only half of the points, after which it replicates them around the centerline, which I took to be coincident with the y-axis. By doing so, I could replicate the points with a single statement:
```    points = map(lambda(x, y): (-x, y), points[:1:-1]) + points
```
This code assumes that the two symmetric halves of the counter will meet in a single point, which should not be replicated. It avoids replication by ending the extended slice `points[:1:-1]` at `1` instead of at `0`. The `-1` causes the points to appear in reverse order after the join, maintaining the symmetry.

• The somewhat obscure ability to define the elements of a tuple as part of a parameter list proved useful in converting a line's endpoints' coordinates into an appropriate `pic` command:
```    def absline((x1, y1), (x2, y2)):
print "line from %f, %f to %f, %f" % (x1, y1, x2, y2)
```

This program made it easy to experiment with shapes and sizes, such as
a circular counter, which has an exponent of 2,
and a counter with an exponent of 3.
It would be uncomfortable to sit at one of those corners.

The foregoing images should make it clear that 2½ is a better choice of exponent than 2 or 3:

Once I had the counter in its final form, I could change the grid lines, previously one scaled foot apart, to be one scaled inch apart:

When I enlarged this image to an appropriate size, I could print segments of it onto individual sheets of paper, tape them together, and give them to the fabrication company.

Finally, let's look at a photo of the completed counter, along with the program's output in the corresponding orientation:

These images show how useful the program was in arriving at the final design.

Experimenting with tile

Having figured out the shape for the counter, I turned my attention to the tile wall behind the stove. Our original design approach involved colored markers and graph paper, but we soon realized how slow that approach was. To streamline the process, I decided to try to write an interactive Python program that we could use to visualize prospective tile designs.

I know a smattering of Tcl and Tk--enough to realize that it is straightforward for a program to generate a large number of rectangular buttons, each of which has an associated action. Accordingly, I decided to adopt a brute-force strategy of assigning a separate button to each tile. The program would have a notion of a ``current color,'' along with a button that would change that color. Clicking a tile would change that tile to the current color.

This strategy did not address the question of output. The reason was that I had available a program named VuePrint, which, among other abilities, can capture the bitmap that a window contains. Such captured bitmaps would be more than adequate as a guide to laying out tile.

The hardest part of the tile-design program turned out to be making each button change its color when clicked. It is easy to associate a function with a button, so that clicking the button calls the function, but that function does not appear to have any easy way of determining the identity of the button that triggered it. For that reason, I could not figure out how to write a single function to implement the notion of ``change my button's color to the current color.''

To solve this problem, I used a bit of subterfuge: I defined a higher-order function that, given a button, would create a function to set that button's color:

```    def setcolor(obj):
def f():
obj["bg"] = color
return f
```
This seemingly trivial function probably took me more time to figure out than the rest of the program put together. Once I had it, however, it was easy to use it to create an appropriate number of buttons, each with its own dynamically created color-setting function:
```    def createWidgets(self):
for i in range(nwidth):
for j in range(nheight):
b = Button(self, bg = "black")
b.config(command = setcolor(b))
b.place(height = height, width = width,
x = i * height, y = j * width)
# and so on...
```
Here, the magic is in the call to `b.config`, which associates with the newly created `Button` named `b` a function that, when called, will set `b`'s color to the current color.

Aside from this magic, the program was straightforward. As with the other program, I did not try to make it elegant or easily maintainable. Nevertheless, it served its purpose in making it possible for us to try quite a number of layouts.

Our first try looked like this:

The large beige rectangles in the upper corners represent maple kitchen cabinets; the inverted T is the range hood, and the gray rectangle at the bottom is the stove's stainless-steel backsplash.

At this point, we realized that we had the dimensions of the hood wrong. We also decided that we wanted to use some red tiles to add emphasis:

If we were making the design by hand, we would surely have stopped here. However, the program was easy enough to use that it allowed us to try other possibilities:

Our final design was yet another small variation on the same theme:

We felt that the blue background and yellow corners suggested sky and sunlight, and the red and yellow tiles near the stove itself suggested fire. The details are less interesting than the finished kitchen:

in which the similarity to the screen shot is obvious. Indeed, the first difference that I notice is the two outlet covers, the locations of which I did not know at the time I wrote the program.

Discussion

Because our kitchen project was unique, and because we were our own customers, the resulting software was of a rather different nature than a commercial project would have been:

• There was no external pressure to use or not use any particular software. The only question was which of the tools that I had available would let me solve my problems most straightforwardly.
• Much of the work wound up being done in programs that already existed: `pic`, `troff`, `dpost`, and `VuePrint`.
• One of the programs that I wrote did only text manipulation and numerical computation. The other made heavy use of the `Tkinter` library. It was easy to learn enough about that library to do useful things with it.
• The two most difficult parts of the solution were at the top and bottom levels: choosing an overall strategy, and figuring out how to write a higher-order function to bind an action to a specific button.
• The choice of language for the first program would not have mattered much, because other programs, which dealt with ordinary text files, did most of the work. The second program, on the other hand, depended heavily on a specific library that, in turn, was tied to a specific language. Two languages, actually, because the tile-design program could surely have been written in Tcl/Tk instead. However, I am less familiar with Tcl than with Python, and I expect that it would have been a good deal more difficult to use Tcl/Tk directly than it was to go through a Python intermediary.

What made Python particularly well suited for this context was the rapid turnaround that comes from an interpretive implementation, and the libraries that have grown up around it.