Tools for generating tabletop cards

I’ve been working on a board game for about a year now. The code name is Calvinball because I make so many rule changes in the middle of a game. It’s a worker placement and resource management game. This post is about my latest attempt to improve the tools I use to print the cards for that game.

I’m pretty comfortable in Visio so I started out with a big Visio document with all the cards in it. This was annoying because I had a zillion instances of everything and if I wanted to change the way one of the icons looked I would have to go retouch all the cards. So I did some scripting in Visio to automate some of that. Unfortunately that made each element complex enough that Visio runs out of memory and starts corrupting things. It’s also not nearly procedural enough for my tastes and made me to a bunch of redundant dragging and clicking.

When I was trying to bring up a second card game (a coop wilderness exploration thing) I tried something completely different. Using Django and Python I generated the cards in HTML and then printed them from the browser. This kinda worked, but HTML is really not meant to have tight registration on its printed output so getting the front and back of cards to line up was constantly a problem. There were also strange scale and background color problems depending on what options I forgot to pick in the print dialog. So this approach wasn’t very satisfying either.

This weekend I started working on a new technique, and so far that’s going pretty well. I build card templates as SVG files in Inkscape. Then I load those with a Python script, process them a bit and dump out a card-specific SVG file. Then I use more Python to combine blocks of card files into pages. Then I turn those pages into PDFs and use PDFtk to make one big PDF with everything in it. At that point a PDF reader (I use FoxIt) can print everything in one batch.

The card templates are pretty straightforward. Here is one of them:

All the information required to render the cards is in one big list of dictionaries. When I get the rest of the cards switched over I’ll make this representation more powerful:

	cards = [
			"params" : {
				"cardtitle" : "Construction Zone",
				"carddescription" : "Players take one victory point when they build a building.",
			"back" : "back_goal",
			"front" : "front_goal",
			"params" : {
				"cardtitle" : "Research Center",
				"cost" : "RRR",
			"back" : "back_goal",
			"front" : "front_goal_donate",
			"params" : {
				"cardtitle" : "Breeding Stock",
				"carddescription" : "Players take one victory point when they opt to not gain a colonist and instead donate it to the galactic core.",
			"back" : "back_goal",
			"front" : "front_goal",
			"params" : {
				"cardtitle" : "One for All",
				"carddescription" : "Players take one victory point when they make a donation.",
			"back" : "back_goal",
			"front" : "front_goal_allforone",

The code to combine the two is too long to paste inline, but the whole python script to do all of this is here. It walks through every entry in the cards list loading the front and back templates for each card. Then it tries to find elements for each entry in the “params” dictionary and replace the text of that element. You may have some difficulty getting this to run on any computer other than mind. Install lxml and svg_stack into Python, and put Inkscape and PDFtk in your path and you should be most of the way there.

One tricky bit is that the node layout for flowing text is different from a single line of text. This function finds the node with the text in it in each case:

def FindNode( root, name ):
	el = root.findall( ".//n:text[@id='" + name + "']/n:tspan", xmlNamespaces)
	if( el ) :
		return el[0]

	el = root.findall( ".//n:flowRoot[@id='" + name + "']/n:flowPara", xmlNamespaces )
	if( el ):
		return el[0]

	return None

It’s also worth noting that flowRoot doesn’t seem to work anywhere but Inkscape. Apparently it was in the final spec for some version of SVG that nobody supports. I use it for descriptions on some cards where I need word wrapping.

The other kind of parameterization that’s currently supported is recoloring or hiding the resource icons to match the cost string on the card definition. That just finds paths under a group with a special ID and sets their style to something specific to that resource type:

resourceStyles = {
	"F" : "fill: #16CD22; stroke: #000000; stroke-width: 0.5;",
	"E" : "fill: #D5FF2D; stroke: #000000; stroke-width: 0.5;",
	"M" : "fill: #404040; stroke: #000000; stroke-width: 0.5;",
	"R" : "fill: #FF69F7; stroke: #000000; stroke-width: 0.5;",
	"C" : "fill: #0000F7; stroke: #000000; stroke-width: 0.5;",
hiddenResourceStyle = "opacity:0"

def SubstCost( root, cost ):
	for n in range( 0, 15 ):
		style = hiddenResourceStyle
		if( n < len( cost ) ):
			style = resourceStyles[ cost[n] ]

		el = root.findall( ".//n:g[@id='cost" + str(n) + "']/n:path", xmlNamespaces )
		for path in el:
			path.set( "style", style )

The card SVG files are all dumped into a directory that contains all the script's output. For my game I write card back and fronts for every card. Most of the cards in my game are double-sided so having a shared back between multiple cards is actually kinda rare. You might be able to do something less spammy. Here are all the files generated by my initial test case:

From there the script builds "pages" where a page is a list of svg filenames. With standard 2.5" x 3.5" cards you can fit 8 cards on an 8.5" x 11.00" page, so every 8 cards forms a page. The pages alternate between fronts and backs with the fronts shown top to bottom and the backs shown bottom to top (to match the duplex feature on my printer.) If there aren't an even multiple of 8 cards blank cards are added to the pages so that each page is exactly 7" x 10". See lines 140-170 in the script for the code that does this pagination.

Using the lists of SVG filenames the script uses a Python module called svg_stack to combine them into bigger SVG files. svg_stack has horizontal and vertical box layouts, but it's pretty easy to combine those two to get a 2x4 grid to fit 8 cards per page. those cards are then written out to an SVG file that looks like this:

From there it's a quick trip through the Inkscape command line to converting that 7" x 10" SVG into a 7" x 10" PDF:
inkscape -f output\page0.svg -A output\page0.pdf

And once I have a bunch of those files I can cat them all together with pdftk:
pdftk output\*.pdf cat output output\allcards.pdf

Then you can print the whole thing in a PDF reader. In FoxIt I have to make sure to turn off scaling and auto-center on an 8.5"x11.0" page when I print:

Hopefully that brain dump is useful to somebody! I sure wish I'd had something like this to read yesterday morning.

Using heightmaps in the Terrain component in Unity 4.2

This weekend I spent some time playing around with procedural heightmap generation in Unity. Since the documentation for these APIs is pretty sparse, I wanted to post about all the traps I fell into and maybe save other people some time.

Here’s the code I ended up with. ChunkId.chunkSize is a Vector2 that contains {100, 100}. Perlin is a noise generator from this LibNoise port to Unity. This is a method on a custom component I added to each of my Terrain GameObjects.

public void UpdateHeight( Perlin noise, float height )
Terrain terrain = gameObject.GetComponent();
TerrainData terData = terrain.terrainData;
Vector3 pos = gameObject.transform.localPosition;

float[,] rHeight = new float[terData.heightmapWidth, terData.heightmapHeight];

float xStart = (float)id.x * ChunkId.chunkSize.x;
float yStart = (float)id.y * ChunkId.chunkSize.y;
float xStep = ChunkId.chunkSize.x / (float)(terData.heightmapWidth - 1 );
float yStep = ChunkId.chunkSize.y / (float)(terData.heightmapHeight - 1);
for( int x = 0; x< terData.heightmapWidth; x++ )
for( int y=0; y< terData.heightmapHeight; y++ )
float xCurrent = xStart + (float)x * xStep;
float yCurrent = yStart + (float)y * yStep;
double v = noise.GetValue( xCurrent, yCurrent, 0 );
rHeight[ y, x ] = (float)(v + 1.0) * 0.5f;

terData.SetHeights( 0, 0, rHeight );

When I ran the initial version of this code, I ended up with gaps all over the place wherever two terrain chunks met. These are all the things I had to learn to fix those gaps:

Trap #1: Heightmap data is duplicated on the edges

This should have been obvious. It was also an easy fix. That's why there is a -1 in this code:

float xStep = ChunkId.chunkSize.x / (float)(terData.heightmapWidth - 1 );
float yStep = ChunkId.chunkSize.y / (float)(terData.heightmapHeight - 1);

Trap #2: Height values must be in the range 0 to 1
The values you put into your heightmap aren't actually "height" values even though they're floats. They are the parameter that will let the terrain system scale the height between 0 and the "Terrain Height" parameter in the editor. Because the noise generator produces roughly -1 to 1 values, I had to scale them like this:

rHeight[ y, x ] = (float)(v + 1.0) * 0.5f;

Values above 1 or below 0 are clamped to 1 or 0 respectively so if you pass them in you will end up with some flattened sections of terrain.

Trap #3: Height arrays are Column-Major

You might have noticed something else that's funny about the line of code I just listed:

rHeight[ y, x ] = (float)(v + 1.0) * 0.5f;

For most of the time I was fighting with this I had the x and y reversed because I assumed that the 2D array expected by SetHeights was row-major just like everything else in the universe. This is the biggest documentation hole I ran into and what cost me the most time.

Trap #4: The "top" neighbor is in the positive Z direction

In order to make the LODs and normals on the transitions between terrain chunks work Unity keeps track of which terrains border which other terrains. You tell the engine about these relationships via the SetNeighbors call. For no particularly good reason I was expecting that left was -x, right was +x, top was -z and bottom was +z. Turns out that I had two of those backwards:

  • Left: -x
  • Right: +x
  • Top: +z
  • Bottom: -z

7 kinds of Media?

I recently watched this video from AWE 2013 by Toni Ahonen on why Augmented Reality is the 8th mass media. The part of his talk that didn’t quite sit right with me is his list of types of Mass Media. Here’s the list (including his new 8th type):

  1. Print (books, pamphlets, newspapers, magazines, etc.) from the late 15th century
  2. Recordings (gramophone records, magnetic tapes, cassettes, cartridges, CDs, DVDs) from the late 19th century Cinema from about 1900
  3. Radio from about 1910
  4. Television from about 1950
  5. Internet from about 1990
  6. Mobile phones from about 2000
  7. Augmented Reality from about now

I have two problems with this list. The first is that software is mostly absent. If I download an app from the app store or install a piece of software off a physical disk isn’t that a kind of media? One of those is arguably “Internet” and “Mobile”, but they’re both basically the same thing and both certainly qualify as “mass”.

My second problem is that the list seems to be an odd mix of content types and distribution mechanisms. Print gets one entry despite having a zillion forms. Audio and video both get two entries even though they’re both distributed in significant ways over the Internet. And Augmented Reality isn’t really either one, it’s sort of a kind of software that might not even be distributed.

I would use a different list:

  1. Print – The written word, including digital words like the ones you are reading right now. This includes still photography and all the flat kinds of art. – from the late 15th century
  2. Audio – Spoken words, music, and other kinds of sound regardless of distribution mechanism. – From about 1900
  3. Video – Moving pictures regardless of the distribution mechanism. – From about 1900
  4. Software – Pretty much anything where you are interacting with an automated system. This became a mass media with the personal computer revolution. – From about 1980

If you need to talk about how this media is transferred you could build a related list of distribution mechanisms:

  1. Physical – Somebody drops a hunk of dead tree on your doorstep or you buy a movie on physical media at a store and carry it home. – Since forever
  2. Radio – An analog or digital signal using radio waves. – Since about 1900
  3. Land Line – An analog or digital signal travelling down a wire or hunk of optical fiber. – Since the late 1800s
  4. Internet – This actually happens on top of land lines and radio, but it abstracts away all of that so well that it probably deserves to be its own distribution mechanism. – Since about 1995 (as a mass thing)
  5. Undistributed – Live performances or one-of-a-kind artifacts. The consumer has to physically go somewhere to experience things that are transferred this way. – Since forever

And if you care about the style of distribution there’s a third list:

  1. Broadcast – One producer, many consumers. The printing press started this, arguably. – Since the late 15th century
  2. Peer to Peer – Many producers, many consumers. For print this would include letter writing. – Since the invention of written language
  3. Many to One – Many producers, one consumer. This is used for things like the census, tax returns, and polls. – Since the invention of governments

Maybe it’s just my engineer-brain talking, but this seems like a much more clear way to express the various types of mass media. The thing is, I’m not sure it’s actually any more useful than that first list. The point of the first list seemed to be to make first Internet companies feel good about themselves. Then later that was expanded to Mobile companies and now it is being expanded to Augmented reality companies. Did anybody ever look at that list and gain any kind of insight?

What do you think? Is this sort of breakdown of media types useful?

Secret Innovation

One of the staples of near-future science fiction is organizations working in absolute secrecy to produce big game-changing innovation. I’ve been trying to come up with examples of this in the real world, but haven’t found any.

A few examples from science fiction: (These are kind of spoilers, but not very good ones.)

  • In Daniel Suarez’s Daemon a character named Matthew Sobel invents a new world order in the form of a not-quite-intelligent internet bot. Even the contractors working with Sobel don’t really know what they’re working on until Sobel dies right before the book begins, causing the Daemon to be unleashed.
  • In Ernest Cline’s Ready Player One James Halliday and his company go complete dark for several years and emerge with OASIS. This was a combination of hardware and software that created a globe spanning, latency-free shared virtual world for the people of earth to inhabit.
  • In Kill Decision (also) by Daniel Suarez, a shadowy government contractor builds automatous drones and deploys them for months before anyone realizes what’s going on.
  • The society ending technology from Directive 51 by John Barnes is a prime example. The book uses nanotech, extreme mental subversion and idealist/zealot manipulation to hatch a world wide society ending event. (via Jake)
  • In Nexus by Ramez Naam somebody engineers nanobots that link people’s emotions. Then some other people develop software to run on Nexus that give them super-human abilities. All of this happens in secret.

(There are certainly more examples than these. Mention them in the comments and I’ll add them to the list.)

In reality it doesn’t work like this, and there are two reasons for that.  The first is that the secrecy is far from perfect and the world gets a gradually better picture of whatever the thing is as it approaches completion. The second is that in the real world every successful innovation is an improvement on a usually-less-successful innovation that came before it.

The most likely counter-example people will bring up is the iPhone. Didn’t it spring fully-formed form the head of Steve Jobs on January 9, 2007?  Well no. Not even remotely. Apple’s own Newton, the Palm Pilot, the HP 200LX, Blackberry, and many more were clear fore-bearers of the iPhone. Apple certainly drove the design of that sort of device further than anyone else had. They definitely improved it to the point that millions of people bought them as quickly as they could be manufactured. They didn’t spring an entirely new kind of device on the world in a surprise announcement. The iPhone was basically a Palm Treo with the suck squeezed out.

Unfortunately as the iPhone demonstrates, “Big game-changing innovation” is not very easy to define. Let’s go with:

A new product or service that is so advanced that society doesn’t have a cultural niche in which to put it.

The Daemon, OASIS, and the swarms of killer drones from Kill Decision certainly fit this definition.  Are there any examples of products or services from the real world that do? If you can think of one, please leave a comment below and tell us about it.

3D Printing and The Humble Toaster

I have been thinking quite a bit about 3D printing lately. Maybe that’s because I’m now surrounded by 3D printed prototypes at work. Maybe it’s because the news in the tech world is full of stories about products and services related to 3D printing. All of this has lead me to two conclusions:

  1. In the short to medium term 3D printing will be something that companies use to provide customized goods to consumers, not something that consumers use directly.
  2. 3D printing won’t take off for in-home manufacturing until a printer can build something like a toaster

A 3D printer is an incredibly powerful tool for prototyping. In the hardware lab at the office we have a couple of Dimension printers, a laser cutter, a PCB mill, a Vinyl cutter, and a fairly complete set of power and hand tools. With those tools, a few McMaster-Carr and Digikey orders, and enough assembly time we can build a prototype of just about anything. A dizzying array of goods has come out of that lab and let us try out things in a few days to a week that would have taken us a few months to a year if we had tried to do the same thing ten years ago.

The problem is that the output of the printer requires additional off the shelf parts and significant assembly time to turn an ABS structure into something functional. This is where I think the toaster is a useful mechanism to talk about.

The pop-up toaster is arguably the simplest appliance in your kitchen. And yet it is also filled with components that are beyond the reach of 3D printing today.  (You can learn more than you ever wanted to know about how a toaster works here.) Here are several challenges that toasters present for 3D printers:

  1. The whole device is heat-resistant. Toasters heat to about 300 degrees Fahrenheit, but the melting point of ABS plastic is only 221 degrees. Clearly printing the toaster body out of ABS isn’t going to work.
  2. The power cord of a toaster contains both conductive elements and insulating elements. There are also insulating elements scattered around inside the toaster. Extrusion printers can handle the insulating elements, but not the heat resistance. Binding printers (sintering or EBM) can handle the conductive elements, but only print one material at a time so they can’t combine the conductive elements with the insulating elements in one object.
  3. “A toaster’s heating element usually consists of Nichrome ribbon wound on mica strips.” Nichrome is actually used in extrusion printers to heat the plastic. I can’t find any reference to either it or Mica being printable, and that would definitely require multiple materials in a binding printer.

None of these problems are insurmountable. Several 3D printers can already print in multiple materials. They just all happen to be different kinds of extruded plastic. Eventually those printers will figure out how to include metal in their parts. Heat resistance is probably a bigger challenge given how the printers work, but in theory they could use materials with even higher melting points so the resulting products could handle 300 degrees without a problem. And the list of materials that can be printed is growing every year. Eventually these problems will be solved.

That brings me back to the first conclusion, however, because I don’t think those solutions are going to come from the low-end printers you can afford to put in your house. Figuring out how to print conductive and insulating materials in the same batch is a hard problem. Heat resistance is a hard problem (for extrusion printers). Printing new and exotic materials is hard problem. These are the sort of thing that researchers are going to chew on for a while, then expensive first generation commercial implementations will need to be built.

As MakerBot eats away at the low end businesses of Objet and Dimension these bigger companies will move further up-market and add some of these higher-end features. Eventually MakerBot and its ilk will get those features too, but that is going to take a long time, probably ten or more years. In the meantime, the only printers capable of printing “advanced” devices like a toaster will be those that cost tens or hundreds of thousands of dollars. Price alone will keep these printers out of the hands (and garages) of individuals for a long time.

The only people who will be able to afford the next generation of toaster-capable printers are companies that use that capital investment as part of their business. That includes hardware labs that use these as tools for prototyping, but also mass-customization companies that build iPhone covers or print MineCraft levels today. These companies can charge a premium for their products because of the customization so they can amortize the cost of the printers (and learning how to use them) over thousands of customers. They will also be the primary market for companies like Dimension and Objet, so those printer providers will have no reason to drop their prices to a level normal people can afford.

One last random thought before I end this meandering post:  The printers that we’re running in twenty years will bear about as much resemblance to today’s 3D printers as my Nexus 4 bears to my first computer (a TI 99-4/A). They will be a combination of binding and extrusion, or something we can’t even imagine now. They will include many elements of a pick-and-place machine to include complex semiconductors in the resulting devices. And, once their utility reaches a certain point, their prices will be in free-fall. Eventually the future of 3D printing is bright. I just don’t think it’s going to happen overnight.