Jekyll2026-03-14T17:32:53+00:00https://wabain.github.io/feed.xmlWilliam BainWilliam Bain[email protected]Working notes: Animating k-d trees2020-01-05T00:00:00+00:002020-01-05T00:00:00+00:00https://wabain.github.io/2020/01/05/animating-kd-treesAmid the post–New Year spate of project roundups, I discovered Luke Pattons’ Canvas Cards project, and through it some mesmerizing procedural animations by Raven Kwok.

I didn’t find much on how Kwok implemented the animations beyond his comment that “the visual’s core structure is a K-D tree”. So I decided to experiment with k-d tree animation myself.

A k-d tree is a binary tree structure that encodes k-dimensional points to define a partitioning of a k-dimensional space. Sequential levels of the tree contain the median points of subsets of the points in alternating dimensions, carving up progressively smaller areas. The trees are commonly used for nearest-neighbor search, but here our goal is to use them to drive visually interesting transformations of geometrical shapes.

Diagram of a 2-d k-d tree
An example of a 2-dimensional k-d tree (via Wikipedia)

It was pretty easy to implement some basic logic to render a 2-d tree to an HTML5 canvas; Wikipedia has some working code snippets which build the tree by recursing over the nodes. In my first attempt to animate it I picked two arrays of points, interpolating between them and rendering the k-d tree that results from each interpolation:

My first attempt

The results are not great, since the lines of the tree are not continuous throughout the animation—as points shift, their positions in the tree will eventually change, causing the lines to jump.

My next idea was to interpolate the values in the tree directly. I build k-d trees for an initial and final set of points and then render trees which tween the values of the isomorphic nodes. The interpolated trees aren’t proper k-d trees, but they’re visually similar and give a nice, continuous animation.

My second attempt

By the end of what had turned out to be a rather long Friday night of experimentation, I had built a moderately engaging little animation. First I switched to coloring the rectangles formed by the tree rather than the lines. Then I dialed up the number of points used to generate the tree and punched up the timings. Finally I switched from the RGB palette I’d used for testing to something a bit easier on the eyes.

The outcome

Having made it this far brought home to me the range of choices that influence these animations’ feel. The selection of color, gradient and texture (there’s a graininess superimposed on Kwok’s images), along with the use of negative space and staggered transitions, do a lot to make Kwok’s animations engaging. The ability to incorporate improper trees in the animation also intrigues me—it’s a natural step from interpolating points between k-d trees to decomposing arbitrary shapes into k-d–defined blocks and back again. My next explorations, if I come back to this concept, will probably take that idea further.

]]>
William Bain[email protected]
CSS Trivia: Rotated table headers in 20192019-10-13T00:00:00+00:002019-10-13T00:00:00+00:00https://wabain.github.io/2019/10/13/css-rotated-table-headerRecently I built an HTML report to aggregate the results of some automated jobs. The table headings being much longer than the table’s contents, I wanted to set the headings at a 45° angle. When I Googled for how to do this with CSS, the answers I got were all years old and involved some complications that seem to have become unnecessary. Since I didn’t find a current guide, I wanted to record what I settled on.

The top Google result for “css rotate table header 45 degrees” is a 2014 CSS Tricks article which rather breezily describes a variation on an earlier technique from another blog post.1 The trail of posts traces back to one from 2009, a very different era in web development. At the time, the best technique was to rotate the header with its top left corner staying fixed, and then to apply what the CSS Tricks article calls “some math stuff using tan() and cos()” to figure out what translation needed to be applied in order for the bottom right of the rotated header to meet the top right-hand corner of the cell below.2

The key to the updated approach is that instead of rotating from the top left and then correcting the horizontal and vertical positioning, we can keep the bottom left point of the header fixed by setting transform-origin. Then we only need to offset the horizontal position of the text by the width of the table column, a constant we’ll already have in our CSS.

2009
2019

This neatly takes care of the core problem of positioning the headings, although we’ll still hit some awkwardness related to having transformed elements in the CSS layout. The example below demonstrates the technique:

Year
Splines reticulated
Ipsums loremmed
Observations on the theory and practice of landscape gardening
2016 120 1,900 25
2017 3,002 14,000 16
2018 20,124 980 48
<style>
  .scrollable {
    overflow: auto;
  }

  .rotated-header th {
    height: 240px;
    vertical-align: bottom;
    text-align: left;
    line-height: 1;
  }

  .rotated-header-container {
    width: 75px;
  }

  .rotated-header-content {
    width: 300px;
    transform-origin: bottom left;
    transform: translateX(75px) rotate(-45deg);
  }

  .rotated-header td:not(:first-child) {
    text-align: right;
  }
</style>

<figure class="scrollable">
  <table class="rotated-header">
    <thead>
      <tr>
        <th>Year</th>
        <th>
          <div class="rotated-header-container">
            <div class="rotated-header-content">Splines reticulated</div>
          </div>
        </th>
        <th>
          <div class="rotated-header-container">
            <div class="rotated-header-content">Ipsums loremmed</div>
          </div>
        </th>
        <th>
          <div class="rotated-header-container">
            <div class="rotated-header-content">
              Observations on the theory and practice of landscape gardening
            </div>
          </div>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>2016</td> <td>120</td> <td>1,900</td> <td>25</td>
      </tr>
      <tr>
        <td>2017</td> <td>3,002</td> <td>14,000</td> <td>16</td>
      </tr>
      <tr>
        <td>2018</td> <td>20,124</td> <td>980</td> <td>48</td>
      </tr>
    </tbody>
  </table>
</figure>

There are a few points worth noting:

  • In this demo I’m using two divs inside each th: a wrapper to set the width of the header cell and an inner div for the rotated text. In theory I think it should be possible to fix the dimensions of the cell by styling the th itself, but I haven’t dug into the specifics of CSS table layout rules to figure out how to make it work.

  • I rely on a vertical-align rule to push the header text to the bottom of the fixed-size th elements. Something similar can also be done with flexbox.

  • I’ve set the hardcoded height of the header heuristically; in principle one could do “math stuff” to get the minimum height that includes the rotated header but in this demo the height of the rotated header is determined by the height of multiple lines of text; getting the necessary parameters dynamically is probably possible on the client side, but requires knowing exactly how the header text will be laid out. This is tricky, particularly if the font size or leading change responsively based on the window size.

  • Part of the rotated headers extend outside the area of the table itself. In this demo I’ve wrapped the table in a scrollable container and made it left-aligned. While the scrollable area of the container includes the protruding headers, if the table is centered within the scrollable container it will be centered according to its own size, excluding the headers. This can push the headers outside the area which is visible without scrolling.

Bonus: Fallbacks

Support for CSS transforms is pretty robust, but what happens if a user agent lacks it? In my case, the problem child was the Outlook web client, which allows embedded CSS in emails, but strips transform styles for what I imagine are security reasons. Without my making special provisions, this resulted in the unrotated row headers overlapping and becoming unreadable.

Fortunately, there’s a simple fix: wrap all of the rules related to header rotation in a @supports(transform: ...) query. Clients not supporting the transform will render an unwieldy but basically readable table. With support for @supports being a bit spottier than support for the CSS transforms themselves, this probably results in some extra user agents rendering the fallback, but for me that’s an acceptable cost for what is basically a progressive enhancement.

  1. CSS Tricks is an excellent resource; the post I stumbled on is the result of having a deep back catalog. As I was preparing this post I used posts from the same author to refresh my memory on how to animate SVG. 

  2. The older posts also skew the shape of the header in order to have its horizontal lines stay parallel with the horizontal lines of the body rows. This is less relevant now that tables with external borders are largely out of style. 

]]>
William Bain[email protected]
Fixing C arrays, four decades later2019-08-10T00:00:00+00:002019-08-10T00:00:00+00:00https://wabain.github.io/2019/08/10/c-arraysOne of the greatest challenges in teaching C is the idiosyncratic rules that govern string and array variables. While teachers can delay this hurdle until the second or third week by focusing on programs that use printf with static strings, it can’t be put off much further. Any C program that takes command line arguments needs to grapple with how arrays, pointers, and strings relate. The first Google result for “C main” introduces main functions with a signature that highlights the pointer/array duality:

int main( int argc, const char* argv[] );

Explaining how to work with the elements of this signature—how to manipulate the string char* argv, the array char* argv[], or the equivalents char** argv and char argv[][]—requires teaching not only the runtime semantics of pointer access, but also the particularities of C array variables and the rules that govern their conversion to pointers, an entirely orthogonal concept which is slippery in its own right. When I finally came across an explanation of the rationale for C’s array semantics in a retrospective by Dennis Ritchie, I found that while C’s approach is tied up with the legacy of long-ago PDP-11 code, it also addresses an inherently tricky language design problem that has taken decades to unravel.1

When Ritchie developed the pre-C variant NB, he allocated two separate areas in storage for an array declaration like char carray[10]: first, the backing array of ten char’s, and second a pointer to them, which the variable carray refers to. The problem with this scheme, it turned out, was that it couldn’t readily be applied to declaring the fields of structured types:

For example, the directory entries of early Unix systems might be described in C as

struct {
	int	inumber;
	char	name[14];
};

I wanted the structure not merely to characterize an abstract object but also to describe a collection of bits that might be read from a directory. Where could the compiler hide the pointer to name that the semantics demanded?

Constrained by the need to mostly preserve the semantics of existing programs, the solution Ritchie hit on was to manifest the pointer at the point of use: only one storage area, for the array content, would be allocated, but an array-type variable in an expression would be treated as a pointer to the first value in the array.

This rule leaves arrays variables, in some ways, as awkward second-class citizens. In proto-C, given arrays defined as char a[10], b[10] it was possible to write a = b to point a to the array defined for b. In the world after Ritchie’s retrofit, this is problematic: according to the rules, b should be treated as a pointer, but if a were treated the same way there would be no coherent way to define assignment to a struct member; in the expression s.a = b, there is no pointer in the struct to act as the target of the assignment. This restriction manifests in the C89 specification as a rather inconspicuous clause restricting the applicability of array variable expressions:

Except when it is the operand of the sizeof operator or the unary & operator, or is a character string literal used to initialize an array of character type … an lvalue that has type “array of type” is converted to an expression that has type “pointer to type” that points to the initial member of the array object and is not an lvalue.

This excerpt also highlights the other set of restrictions that came with Ritchie’s rule. For the sizeof operator, the array is not treated as a pointer, since that would leave no way to determine the size of the underlying storage. That means that when switching a variable from a statically sized array to an unsized pointer, the meaning of the sizeof operator changes, a trap that makes it easy to introduce bugs when refactoring. C compounds the problem by introducing the empty array notation, as in argv[][], as syntactic sugar for a pointer **argv, an affordance that Ritchie ultimately concluded “serves as much to confuse the learner as to alert the reader.”

It might seem that, like many of C’s quirks, the array semantics are just an unfortunate legacy. But I think Ritchie was right to insist that, relatively speaking, C’s semantics ultimately provide “a uniform and simple mechanism” for array manipulation. Inspection of how some more recent languages handle arrays reveals a fundamental impedance between modeling arrays as scalar pointers and as sized regions of memory.2

Go follows C in allowing for arrays to be declared on the stack or inline within a structure, without extra pointer indirection. Unlike C, it distinguishes clearly between array-typed variables and pointers to arrays, defining array assignment as a value copy which works for both variables and struct members (see below).

However, Go’s uniform treatment of arrays breaks down when moving from statically sized arrays to dynamically sized slices. While they borrow the C-style empty array syntax, Go treats slices as a fundamentally different entity. Assigning the value of one slice to another happens by reference. As with C’s sizeof operator, this is a common vector for bugs: a simple refactoring to remove a constant bound, if not done carefully, can completely change a program’s semantics.

package main

import "fmt"

type aggregate struct{ i [4]int }

func main() {
	var a1, a2 aggregate
	var t1, t2 [4]int
	var s1, s2 []int

	// structs: copy by value
	a1.i[0] = 5
	a2.i = a1.i
	a1.i[0] = 10

	fmt.Printf("%v; %v\n", a1, a2)
	// ↪ {[10 0 0 0]}; {[5 0 0 0]}

	// arrays: copy by value
	t1[0] = 5
	t2 = t1
	t1[0] = 10

	fmt.Printf("%v; %v\n", t1, t2)
	// ↪ [10 0 0 0]; [5 0 0 0]

	// slices: copy by reference
	s1 = t1[:]

	s1[0] = 15
	s2 = s1
	s1[0] = 20

	fmt.Printf("%v; %v\n", s1, s2)
	// ↪ [20 0 0 0]; [20 0 0 0]
}

There is an alternative, which, to my knowledge, is best exemplified by Rust. Like Go, Rust allows fixed-sized arrays to be stack allocated or directly embedded in an aggregate type. Fundamentally, it treats dynamically sized arrays—including subarrays of a fixed-size array—as ordinary values, with no automatic pointerization. It accomplishes this by restricting how those variables are actually used, forbidding values of types that do not have the Sized trait from being allocated on the stack. The result is that the alias-instead-of-copy behavior of slices ends up being explicit, and therefore also benefits directly from Rust’s checks against modification of aliased data:

#[derive(Default)]
struct Aggregate {
    i: [u8; 4],
}

fn main() {
    // structs: copy by value
    let mut a1: Aggregate = Default::default();
    let mut a2: Aggregate = Default::default();

    a1.i[0] = 5;
    a2.i = a1.i;
    a1.i[0] = 10;

    println!("{:?}; {:?}", a1.i, a2.i);
    // ↪ [10, 0, 0, 0]; [5, 0, 0, 0]

    // arrays: copy by value
    let mut t1: [u8; 4] = Default::default();
    let t2: [u8; 4];

    t1[0] = 5;
    t2 = t1;
    t1[0] = 10;

    println!("{:?}; {:?}", t1, t2);
    // ↪ [10, 0, 0, 0]; [5, 0, 0, 0]

    // slices: borrowed reference
    let mut s1: &mut[u8] = &mut a1.i;
    let s2: &mut[u8];

    s1[0] = 15;
    s2 = s1;
//  ------- borrow of `*s1` occurs here
    s1[0] = 20;
//  cannot use `*s1` because it was mutably borrowed

    println!("{:?}, {:?}", s1, s2);
}

Rust’s approach does come at the expense of an expansion of the language space; beside the restrictions on unsized type usage, both the the type system and the runtime representation of pointers had to be extended to accommodate these types, the former to handle values with attributes (array size or concrete type) left indeterminate and the latter to allow fat pointers to expose the missing information at runtime. However, it creates semantics that effectively generalize fixed size, dynamically sized, and embedded arrays, with the special-casing visible to the user mostly restricted to the hard constraints of the computer architecture.

After some reflection, I believe Rust’s approach is about the most general possible in a language that exposes array allocation on the stack. Ritchie sacrificed clarity, as well as generality of the assignment operator, for a backwards-compatible and uniform treatment of variable and struct fields. Go sacrifices a uniform treatment of arrays and slices in order to maintain primitives that can be efficiently manipulated. Rust restricts only the use, but not the expression, of patterns that can’t be easily accommodated by conventional stack allocation.

As an academic exercise, it is possible to imagine a fully general array semantics in a language, like Go, that does not have a strict stack/heap distinction. In Go, locally declared variables are already heap-allocated if they could be used by reference outside of the local stack frame. It’s possible to contemplate leveraging this to implement copy-based semantics for dynamically sized slices, with non-pointer assignments copying to the heap and users taking the address of a slice explicitly to assign by reference. The ABI already supports passing pointers for nominally non-pointer values in order to support closures; this could be extended to accommodate heap pointers and concrete sizes to return dynamically sized slices. However, given the impracticality of a language that prioritizes the (potentially much more) expensive access pattern by default, I think the design space Ritchie wrestled with has reached a stable point.

  1. This is not to make any absolute claims to priority. Ritchie notes that languages contemporary to C, like Pascal and Algol 68, also had problematic array facilities, and no mainstream language in the interim that I am aware of has made the kind of design choices I describe here—not Ada or C++ or D, not reference-centric languages like Java and most scripting languages, and not the mostly immutable functional languages and Lisps. I’d be interested to hear about earlier precedents. 

  2. For the purpose of this post, I’m going to ignore stack-allocated variable-sized arrays, although Ritchie alludes to them briefly and Rust is growing partial support to match C’s. Suffice it to say that because variably sized allocations can overflow the stack—with their safety depending on non-local factors like function parameters and the use of temporaries—they should be treated as a much more niche tool than array allocation on the heap or static stack allocation. I also consider the optimization complexity that arises from C’s propensity for pointer aliasing, another concern Ritchie highlights, to be more of a problem with pointers than with array semantics. 

]]>
William Bain[email protected]