Last week, I needed create a scale drawing of my basement floor plan. My license for OmniGraffle Professional are long since out-of-date. I didn’t want to pay $200 for a new license or even another $75 if I can dig up one of my old license keys. So, what’s a hacker to do? Roll his own (on top of Zach’s Vecto library).
My first cut worked, but was pretty ugly:
(interior-wall :start (cons (- 91 31 1/2) 0) ; hose cover
:north 18
:east (+ 31 1/2))
(interior-wall :start (cons 91 (- 103 27 30)) ; storage wall
:south (- 103 27 30))
(interior-wall :start (cons (+ 91 83) 103) ; fish-tank wall
:to '(91 . 103)
:south 27)
...)
I ended up with large calculations like that (- 103 27 30) you see there. It worked. I got an okay looking floor-plan out of it. But, it was obvious that it needed some rethinking.
My next thought was turtle graphics! Tell things where to move. Tell it to start drawing an interior wall. Tell it where to move. Tell it to start drawing a window. Tell it where to move. Tell it to stop drawing a window. Etc. This has possibilities, especially for an internal representation (which I need because I want to autoscale the drawing to fit on a sheet of paper well, so I can’t start drawing until I’ve determined the extents). However, it seems awkward to go with all of the start/stop commands. I am thinking of going more in this sort of direction:
;; going to make some simple functions that use feet and inches instead
;; of units. If you want centimeters and meters, go for it.
(flet ((feet (n) (* n 12))
(inches (n) n))
(with-floor-plan (#P"floor-plan.png"
:max-width 8.0 ; maximum width of output (units)
:max-height 10.0 ; maximum height of output (units)
:dots-per-unit 300.0 ; resolution of output
:grid (inches 6)) ; size of background-grid
(compass :north 180.0) ; draw north indication 180 degrees CCW from right
(exterior-wall ; start drawing an exterior wall
:closed t ; that closes at the end
(left (feet 10)) ; extend wall left 10 feet
(up (feet 6)) ; extend wall up 6 feet
(window
(up (inches 30))) ; draw 30" window
(up (inches 8)) ; draw 8" wall
(door :starts :hinge-side ; or :latch-side
:opens :left ; or :right seen from direction of motion
(up (inches 30))) ; this door goes up, so :left is to
; the left of the page
(up (inches 8))
(right (feet 10)))
(left (feet 4)) ; move four feet to the left
(interior-wall
(up (feet 5))
(right (inches 8))
(door :starts :latch-side
:opens :right
(right (inches 30)))
(right-to (feet 0))) ; move to an absolute left-right coordinate
(move (feet 2) (feet (+ 2 1/2)))
(label "Bathroom")))
Has anyone tackled this problem well already? Or, have any suggestions of how to improve this?
Not CL, but have you tried xfig? xypic?
I haven’t used xfig in years. I never really cared for it. Maybe it’s gotten better in the last ten years, I’ll peek at it again. And, I’ve never used xypic (unless you mean the LaTeX package… and I can’t imagine trying to use it to draw floorplans).
The point is not if there is a package X or a software S to draw floor plans. The point is the implementation in CL of a DSL for that purpose. The idea is quite nice. But you can still borrow some ideas from the LaTeX package tikz for develop a flexible coordinate and path description language.
TikZ’s primitives look fine. I do like the stringing together of elements with –. I may have to get into reader macros or super fancy recursive macros to get something like:
for something that goes up 5, right 10, diagonally up 5-right 5, down 10, left 15.
fyi, Harvey’s been pushing TikZ at me for a while now. 🙂
The idea of a DSL for drawing floor plans is kind of interesting. I’ll watch to see where you go with it. On the other hand, if you just wanted to create a floor plan for your basement using any software, you might try the open source package SweetHome3D at http://www.sweethome3d.eu. It’ll give you a scale drawing, including the width of the walls.
I tried using SweetHome3d about six months ago to map out one, small, square bedroom. I suppose it worked, but I didn’t like any of the output or the input.
Patrick, I’ve been working on something similar, except for laying out tiles. I started out with the functional geomtery language by Frank Buss and others, and modified it for my purposes. Mainly what I wanted to do was reduce the need to be entering coordinates all the time. This is a problem with many graphics libraries, forcing you to work at a very low level. I wanted to be able to say “Attach a triangle here the same size as that square over there”. Or perhaps “Generate a border around this object made by conactenating the required number of this other object”. I want the DSL to do the math for me and let me think more in terms of the problem domain. It’s not ready for prime time yet, but email me if you want to know more.
In any case, that’s what I would recommend: think about how you would describe the plan to an expert, e.g. an architect. For example, you would probably assume that the walls are parallel and meet at right angles unless otherwise specified, and that might make it possible to generate the plan without putting in so many numbers. Also, in real life, you would measure things relative to walls and corners, rather than in absolute coordinates, and it would just be obvious what’s inside and what’s outside, and that should help reduce the amount of info you need to input.
Yes, I think for walls, one probably often wants to say, the wall is 10-feet long, then bends and goes 8-feet. But, certainly, I would want ways to store (push/pop) new base positions so that I could build some portions relative to the room they are in instead of relative to the global origin.
I had thought about trying to specify rooms in terms of volumes instead… this room is the union of a rectangle this big and a rectangle this big where the bottom-left corner of the first rectangle is two feet further down the page than the bottom-right corner of the second rectangle. This makes inside-measurements vs. center-of-wall measurements easier. It makes marking exterior walls vs. internal walls harder unless one assumes that the exterior walls are obviously all walls on the exterior of the drawn portion. It does make doors easier though with things like: this room’s door is five feet left of the bottom right corner and opens into the room.
Oh yeah, one other thing: you can draw arbitrarily complex 2D graphcs with Inkscape, an awesome free application for generating .svg files. But it seems you’re also interested in making a DSL, so code away.
Inkscape is okay. I’m much more comfortable in OmniGraffle or Gimp, but Gimp is fixed-resolution. I suppose I should get used to Inkscape.
Well, my output media are either DXF files (for CAD) or OpenGL (using cl-opengl if I’m in Lisp). Regardless of my solution (whihc involves essentially rendering to Lisp forms so that I can pre-compute the output for either media without having to do calulations during the output), you might try looking up some of the CAD solutions to this problems for ideas. There are scores of CAD packages out there that do the same thing that you’re (and I’m) trying to do. And all (decent) CAD stuff can do things like relative measurements. You’re kinda trying to reinvent the wheel (not that I’m opposed ot that sort of thing).
What CAD stuff? I looked at the Cliki, and the only entry under CAD is for electronic circuit design. I look every five or six months for decent, cheap 2D/3D CAD stuff for designing woodworking projects, but never come up satisfied. Any recommendations?
You mention that your output is DXF or OpenGL. Your output from what? I’m having trouble Googling you by nym. (Actually,
and both turn up lots of hits about cl-opengl where I cannot find in the body of the page at all.) I’d be interested to see what you’re working on.I have been considering an cl-opengl floor-planning tool. I thought an intermediate language that produced nice output files was a simpler starting point for that since I wouldn’t have to deal with fonts in cl-opengl or keyboard input or mouse selection and stuff that would distract from what I really want right away: a nice looking floor plan.
Pat,
CAD is pretty well locked up into expensive stuff like AutoCAD, or cheapie programs that do little.
I mean programs like AutoCAD, not necessarily Lisp code. I meant for you to look over how those programs work, and make your stuff work that way (because the CAD guys have done all this before). Look for AutoCAD packages. AutoLisp isn’t Common Lisp, but it’s close enough to figure out what to do. Look in places like this:
http://www.cadinfo.net/scripts/lisplib-software.cfm?areano=56
I’d also look at tutorials for AutoCAD or SolidWorks to see how they enter things like absolute coordinates and relative coordinates in their command lines.
What I wanted was very similar to what you wanted: a DSL for some graphic operations that I could sue to write programs that output to either OpenGL (for interaction), or DXF (to send to other people).
My impetus is that I’m designing a new house. So I wanted to be able to use Lisp forms to describe the house. Didn’t make it there the first time — I had Lisp code that described it. I have a pretty good background in OpenGL (been doing it since it was IrisGL), but little in CFFI so I’m of little use to the cl-opengl guys. And I also have done a lot of stuff in AutoCAD, and have a bit of code lying around that’s a sort of bad DSL for DXFs. really it’s just a with-open-dxf kind of thing and functions that write out the various elements.
So I had a program that drew my house, with code that looked like:
(if (wire w)
(draw-door-w x y z a r th)
(draw-door-t w x y z a r th)))
This isn’t so good, because I now have to write both the wireframe function and the texture function. And I’d have to add another in order to do DXf output.
So my solution is to do something like:
(add-line yadda yadda) ...)
to create an intermediate format. Then I’d instruct some renderer to use that format for its output. That output could be to my standard OpenGL model viewer in either wireframe or textured mode, or a DXF renderer, or PDF, or whatever.
You probably won’t find too much of my code out in the world. Though I’ve been doing code since the mid-70’s, I’ve only been doing Lisp for a few years. And most of my stuff is copyrighted by my employers.
I’m still not satisfied with the intermediate-forms thing. But, I don’t see a way around it if I want to scale things to fit perfectly at a fixed resolution (unless I make the user pre-determine the needed scaling). I think an alternate approach to the intermediate-forms would be to do things with generic functions that specialize off the renderer. So, you might have:
...
(draw-line renderer pt-a pt-b :weight 2)
(draw-arc renderer center angle-1 angle-2 :weight 1))
Then, you would specialize draw-line and draw-arc based on the renderer.
...)
(defmethod draw-line ((render cl-pdf-renderer) pt-a pt-b &key (weight 1))
...)
Still cogitating…. Thanks for the input.
Hi Pat,
Would you think about putting the code up on the site when you’re done?
I quite like the idea of being able to describe a house plan in a DSL. I’m thinking about doing the same thing as you (building a house) and would like to be able to generate a floor plan from code.
I’ll check back every now and then.
Thanks
I certainly will post the code. My immediate need for it has passed. So, I may post the partial stuff sooner if I don’t get back to it.