Z-Order Curve Visualization

2 minute read Published: 2021-05-30

A visualization of a 3D Z-order curve. Doesn't really work on mobile - use the mouse to rotate the view.

A friend of mine asked me about optimizing matrix multiplication. One way to obtain better cache locality is to use space filling curves.

I made this visualization using WGLMakie and JSServe.jl in Julia. We can convert between points in \(\{0,\ldots,2^{3 \cdot n}-1\}\) and \(\left(\{0,\ldots,2^n-1\}\right)^3\) by interleaving the bits. This is described in more detail on the Wikipedia page. The zoc2p function implements this mapping.

function zoc2p(zoc, digits_per_coordinate, dim)
    p = digits(zoc, base=2, pad=dim * digits_per_coordinate)
    mapslices(reshape(p, :, digits_per_coordinate); dims=2) do x
        sum(2^(i-1) * x for (i,x) in enumerate(x))

Here, dim is the number of dimensions of the z-order curve and digits_per_coordinate corresponds to \(n\). We then enumerate all points in \(\{0,\ldots,2^{3 \cdot n}-1\}\) and send them through the function to obtain the corresponding point in 3D space:

v = vcat([zoc2p(i, digits_per_coordinate, dim)' for i=0:2^(digits_per_coordinate*dim)-1]...)

Here's the full plotting function:

function plt()
    set_theme!(resolution=(800, 800))

    app = App() do s::Session
        digits_per_coordinate = 3
        dim = 3
        v = vcat([zoc2p(i, digits_per_coordinate, dim)' for i=0:2^(digits_per_coordinate*dim)-1]...)

        xs = v[:, 1]
        ys = v[:, 2]
        zs = v[:, 3]
        progress = (0:length(xs)-1)./(length(xs)-1)

        fig, ax, splot = lines(xs, ys, zs, linewidth=50, color=progress, colormap=:rainbow)


    page = Page(offline=true, exportable=true)
    page_html = sprint() do io
        show(io, MIME"text/html"(), page)
        show(io, MIME"text/html"(), app)

    write("export/index.html", page_html)

JSServe then allows us to export the plot as a static webpage, and it's the one you see above embedded as an iframe!