I think one of the reasons I have such a hard time creating artistically is simply because the search space is so overwhelmingly vast. Even with the physical constraints of a 4x6 postcard and a limited set of colors, the possibilities are still endless! And since I have zero artistic background, there’s not a whole lot of intuition I can fall back on for navigating this space more efficiently. What I do have though are my preferences for what I like and don’t like and since those don’t provide much of a gradient, I need some additional constraints. This is effectively what I noticed last year once I committed to using the LBM simulation results, the hit rate for ideas I actually liked increased significantly. When the bounds are defined by physics rather than my lack of artistic creativity, I was able to reach a place I liked. I suppose this isn’t too different from motorsport in that constraints foster a level of creativity not otherwise possible. I guess using CFD as an artistic crutch is fine?
I decided to pursue a similar path again for this year’s PTPX, instead of an LBM foundation though I decided to use my all-time favorite: panel methods. Usually taught at the end of an intro level aerodynamics course, panel methods are a family of numerical schemes that were the pinnacle of CFD when RAM was measured in kilobytes. Although panel methods are “old”, under certain circumstances they are just as useful as a full CFD simulation while requiring several orders of magnitude less compute resources. Because of this, they’re still used extensively during the initial MDO phase. They’re also a gateway drug to modern CFD.
The basic approach involves superimposing various surface distributions (singularities) across small geometric “panels” and then enforcing a set of boundary conditions. The end result is usually a linear PDE in the form of Laplace’s equation. If you’re curious about the details, I highly recommend Low-Speed Aerodynamics by Katz/Plotkin. It’s the definitive reference on the subject and one of my favorite books (or just play around with XFOIL / XFLR5).

XFLR5 | source
With the physics now defined, what should I actually do? Absent any real geometry I started out by randomly placing the aerodynamic primitives (sources, sinks, vortices) onto the postcard domain and was instantly excited by the initial results. After a few evenings of iterating, I ended up with a pattern I liked.

One thing I’m particularly happy about with this year’s batch of postcards is that each postcard is completely unique. Other than a few fine-tuned guardrails, the location of the aerodynamic primitives, the boundary conditions, the seed points used for the streamlines, and the jitter added to the streamlines are all randomly generated. Each recipient will get an original “artwork”. It feels good to finally be living up to the algorithmic/generative aspect of plotter art.
Another element of this year’s design that was fun to improve upon was reducing the print times through better path optimization. Since each streamline is a separate Runge–Kutta integration across the velocity field, I’m left with a bunch of paths that need to be plotted. The total distance of these printed (pen-down) paths is the print distance, while the distance traveled between these paths (pen-up) is the travel distance. And since each postcard has a fixed print distance, I’m left with reducing the travel distance.
There are a bunch of ways we can go about reducing the travel distance. If we look at just one color from a single postcard, the worst case scenario is simply plotting the streamlines in the order in which they were integrated. Given the seed points were randomly generated, the end result is a pile of spaghetti (below, top left). A greedy approach that picks the next closet path start point to the current end point produces something considerably better (below, top right). We can still do better though. A greedy approach that allows paths to be printed in reverse (the next path is one which has the shortest distance to either the start or end point and reversed accordingly) yields additional improvements (below, bottom left). And finally, if we take the greedy + direction flipping approach and run the 2-opt heuristic over it, we get our last bit of improvement (below, bottom right).
Again, the paths above are for just one color from a single postcard. If we generate 1k postcards and aggregate the print and travel distances, we can get a better idea of the performance gains. Note that the total print distances below have been normalized between 0% and 100% while the travel distances have been normalized against the print distances.
We can see that random ordering is clearly the worst, for every unit of distance printed, it requires 2.71 units of travel. The 2-opt refined greedy approach though squeaked by with a travel ratio of 0.29 which is nearly a 90% reduction! I could of course just run something like Concorde, but I don’t want another dependency. My entire plotter stack right now is a single Jupyter notebook that pulls in NumPy/Matplotlib and outputs raw G-code. It’s nice. Anyway, to my recipients this year, I hope you enjoy your postcard!
