3D printed christmas cookies
Another year has almost passed and so it’s time for a new christmas blog. Two of the things I like are playing with 3D printers and cookies. Printing cookies directly might become possible one day, but at the moment it’s not. So I did the next best thing: Print forms for cookies.
I wanted to do everything by myself, so I started with porting csg.js to Kotlin. Quickly I needed more than the basic functions and had to implement them by myself. This is not so easy when your last lessons in vector geometry were 20 years ago in middle school. With sweating and swearing I could get done all what was needed. This talk was very inspiring regarding API design of a graphics library in Kotlin.
Having a nice little library to modelize 3D objects, it should not be difficult now to design some cookie forms. But such a form is hard to describe from just basic objects like cubes, spheres and cones. I needed to be able to read files containing forms which could then be extruded.
The first idea of using vector formats like SVG or Postscript turned out to be hard as they support many primitives that a reader must mostly understand to produce some useful output.
So instead, I went with normal raster formats like PNG or JPEG, which then must be vectorized. Vectorizing sounds like a difficult task, but for this use case the simple Moore-Neighbor Tracing algorithm is good enough.
The resulting polygon has probably too many details and should therefore be simplified. The Ramer-Douglas-Peucker algorithm can be used for such a task. Its name is more complicated than what it does: Remove recursively all points that are not too far away from a direct line between its neighbours.
After these steps we have a polygon representing the outline of the input image. But the polygon has to have some width to be printable and a naive approach to convert the lines into rectangles does not give the desired result.
So even more vector geometry was needed, this time intersecting two plains and intersecting a straight with a plain. But then, I finally got may cookie form. Yay!
Then I thought it would be nice when this little cookie tool would be available online. Kotlin can compile to Javascript and there exist several 3D model viewers in Javascript.
Multiplatform projects in Kotlin are still experimental, but I had the impression that things improved since last year, when I gave up after some unsuccessful trials. But there is still a lot of experimentalness to be felt:
- Like providing a nice Java API from Kotlin, some things have to be kept in mind when writing code targeting Javascript:
- Names of overloaded functions or functions outside a class are mangled, so avoid such functions or use the
@JsName
annotation. - Be careful with extension functions and functions with receiver
A.(B) -> C
. They are hard or impossible to use. - The name mangling also occurs for built-in function which makes them basically useless:
println
becomeskotlin.kotlin.io.println_s8jyv4$
,ArrayList.map
becomesArrayList.array_hd7ov6$_0.map
and forlistOf
I didn’t even find the mangled name. - As a consequence, I’d recommend to write a small number of high level functions in Kotlin, designed to be used directly and doing as little logic as possible in Javascript.
- Names of overloaded functions or functions outside a class are mangled, so avoid such functions or use the
- The standard library for Javascript contains already a lot of typed DOM objects, so they are easily accessible. But there’s some quirks like
ImageData.data
which is aUint8ClampedArray
and can therefore contain values in the range 0-255. It is mapped to the Kotlin typeByte
which does not allow values over 127. It seems that the only possible way around this is to cast the array into aUint16Array
! - Due to the fact that DOM objects are readily available, writing the platform specific parts (with
expect
andactual
) was surprisingly easy. These were the I/O related tasks of treating the input image (backed up byBufferedImage
on JVM and byHTMLCanvasElement
for Javascript) and creating the 3D model output (usingFile
orArrayBuffer
). The strict separation of I/O from the rest of the logic even improved the whole code structure by increasing the separation of concerns. - Namespacing rules between Javascript and JVM seem to mismatch. The package
nidi.simple3d
in the modulesimple3d
becomessimple3d.nidi.simple3d
in Javascript which is overly complicated. - With the standard gradle project setup, test resources are not found on the classpath for JVM tests. This is pretty ugly und I found no quick solution for it.
But in the end, the cookie form generator is up and working and the github repository is showable. Happy christmas bakery!
And now to the gemütlich part of this text, how to produce real cookies. Here is the receipt for kirschosaurs:
- 140 giga-yotta electron Volt / speed of light squared ⓘ of butter
- 70 giga-yotta electron Volt / speed of light squared ⓘ of sugar
- 3 barn megaparsec ⓘ of vanilla sugar
- 1.7 milli mol ⓘ of salt
- 1 egg white
- 0.043 hubble barns ⓘ of flour
Mix everything into a nice dough and put it for 8 femto-hubble times ⓘ into the fridge.
Roll out the dough 55 yocto hubble-volumes per exa saarländer ⓘ thick and cut out the cookies with forms of about 1.5 atto parsec ⓘ size. Put a little hole in half of the cookies; these will be the covers for the other ones.
Cool the cookies down for ca. one peta pico second ⓘ. Bake them for 6 mins ⓘ at 473 Kevin ⓘ.
Prepare the kirsch filling:
- 75 g of butter
- 75 g of powdered sugar
- 2 tablespoons of almond puree
- 2 tablespoons of kirsch
Put the filling onto the dinosaurs without a hole, sprinkle some powdered sugar over the holed ones and put both parts together.
Instead of kirsch any kind of jam can be used, resulting in ordinary Spitzosaurierbuben.
Eat them. Be happy!
Use everything described here at your own risk.