SVG Jigsaw Generation in Clojure
[Cross-post from Bonnie Eisenman’s blog at https://blog.bonnieeisenman.com/projects/clojure-puzzles/. Bonnie is often found at the NYC Resistor craft nights]
I spent the last week learning Clojure and generating jigsaw puzzles as part of my one-week programming retreat at the Recurse Center.
Why jigsaw puzzles? I was motivated by two things: first, I wanted a good-sized language-learning project. Secondly, I was heavily inspired by the amazing, beautiful, intricate jigsaw puzzles produced by Nervous Systems and wanted to experiment with similar-ish generative methods. (I’m a sucker for generative things and hadn’t played with generative algorithms too much before.) If you want a crazy cool puzzle, seriously, go buy one from Nervous.
I think they turned out well!
Because I have access to NYC Resistor’s laser cutter, the obvious thing to do was generate SVGs which I could then laser cut. If you haven’t worked with SVG before, it’s an XML-based format for describing vector graphics. It’s pretty easy to generate “by hand”.
Here is what an SVG looks like, if you open it up with a text editor:
<svg width="100" height="100">
<circle
cx="50"
cy="50"
r="40"
stroke="green"
stroke-width="4"
fill="yellow"
/>
</svg>
This produces a yellow circle, centered at (50, 50), with a radius of 40, and a four-pixel green outline. You can view an SVG file in any web browser, or edit it in an editor like Inkscape.
See? Easy-peasy.
I started by generating a “classic” jigsaw puzzle shape, and figuring out how to tile it.
But I wanted something more interesting than just a grid of similar puzzle pieces! My next step was to use a Voronoi diagram to draw more irregularly-sized polygons around “seed” points. At first this created some amusing failures:
Oops. This is what happens when you draw points at (x, x) instead of (x, y). Let’s fix those coordinates.
If we replace those straight lines with puzzle-piece edges, we get something that starts to look like a more interesting puzzle. There are still obviously flaws to be ironed out here (e.g. edge overlap).
I wanted to make more novel puzzle piece shapes, though, so I turned to the SVG path
type. You can draw Bézier curves in SVG pretty easily:
<path d="M 0 0 C 0 -100 50 -100 50 0 S 100 100 100 0"
stroke="blue"
fill="transparent"
transform="translate(0 400)"/>
OK, here’s what it looks like when we replace our puzzle piece shape with some random-ish curves:
Add more curves and it gets even better!
I also experimented with variations on how to place the seed points for my puzzle generation. Here’s one that’s based on a circular point distribution.
Now I had some monstrously-irregular puzzle pieces to play with. Cool! I wanted to take it one step further by implementing whimsy pieces. In jigsaw jargon, a whimsy piece is a themed, recognizably-shaped puzzle piece. They might be butterflies or people or letters or…you name it!
I modified my puzzle-generator to clear space for a whimsy piece, first testing it with circular whimsy pieces.
Then, using a kd-tree, I identified the whimsy piece’s nearest-neighbors and connected it back to the rest of the puzzle. Here’s a cat!
And, finally, I took these files over to NYC Resistor and lasered them.
It took a group of us about ninety minutes to solve the cat puzzle. Not bad for four days’ work!
All of the code is available on Github at bonniee/svg-puzzle-gen. (It’s my first Clojure program, so I’m sure there are plenty of non-idiomatic things happening there.)
Dependencies / thank-yous:
- trystan/voronoi-diagram
- abscondment/clj-kdtree
- Nervous Systems and their associated blog posts
- Living Clojure, the textbook I used to learn Clojure
- Recurse Center, for providing an inspiring and uplifting space in which to explore/learn/play
- The various people who pair-programmed with me on this puzzle! You’re all awesome 🙂
Testimonials from playtesters:
- “This is awesome!”
- “This is horrible!”
- “This is amazing! And by amazing I mean terrible!”
- “Why are all the puzzle pieces the same ???”
- “Is this supposed to be evil?”
- “How can I get one?”
Sorry, the comment form is closed at this time.