Aaron Christiansen

@aaronc.cc

Software Engineer in Leeds, UK. Web, Ruby, Rust, and electronics. he/him Cross-posting to Mastodon: @aaronc81@ruby.social

Working on this modelling tool again - now you can define *recursive* operators, which lets you construct complex layouts of children based on each of their geometries. Here's a `stack` operator which arranges all children next to each other, even of different sizes, with a gap between each.

A code editor showing this code:

```
operator stack(gap = 0) {
    if ($children == 1) {
        children();
    }

    if ($children > 1) {
        child = children(0);

        translate([child.max_point.x + gap, 0])
        stack(gap = gap)
        ...children([ 1 : $children - 1 ]);
    }
}

stack(gap = 0.5) {
    cube(1);
    cube(2);
    cube(3);
}
```

Besides that, a model viewer shows three cubes of increasing size, with a small gap between each.
Aaron Christiansen@aaronc.cc · 10mo ago

For a bit of polish and ease-of-experimentation, there's now a basic editor! Here's one possible implementation of a rounded rectangle. Being able to define a new operation that inspects the geometry, like this `rotate_around_centre`, feels rather useful to me.

It's day #100 of Loconundrum! Thanks so much to everybody who's played - it's been lovely to hear from people enjoying the game. To mark the milestone, you can now play all 100 past puzzles from the new Archive! Fill out a missed day, or catch up on puzzles from before you started playing.

Screenshot of the new Archive page, showing a colour-coded calendar of different puzzle days

Here's a peek behind the curtain at Conductor, the thrown-together dashboard I use to schedule Loconundrum puzzles. It used to automatically pick at random from a pool, but once I ran out of the major stations, I started scheduling puzzles manually instead so I could test things out a bit more.

Conductor UI. There are filters "Show all previous days, reveal today/tomorrow/future days" and a tools section with "All CRSes to date", "Quick check", and a "re-run routing" button. Underneath is a list of all stations queued up, with buttons to reorder or edit them.

If you played Loconundrum today (puzzle #40, 2026-04-24) before 7am BST, there's a chance you may have got the wrong puzzle due to a caching issue. If you got Birmingham New Street, you were affected. It should be fixed now, please load up Loconundrum again and have another go! Sorry about that 🚂

Falling in love with inline snapshot testing My usual problem with snapshot testing is that you need to find the snapshot files to learn the expected output of a test - but with inline snapshots, the expectation is in the test code, which can be updated automatically by the snapshot tool. Neat!

#[test]
fn test_no_subcommand() {
    let test_env = TestEnvironment::default();
    test_env.run_jj_in(".", ["git", "init", "repo"]).success();
    let work_dir = test_env.work_dir("repo");

    // Outside of a repo.
    let output = test_env.run_jj_in(".", [""; 0]);
    insta::assert_snapshot!(output, @r#"
    ------- stderr -------
    Hint: Use `jj -h` for a list of available commands.
    Run `jj config set --user ui.default-command log` to disable this message.
    Error: There is no jj repo in "."
    [EOF]
    [exit status: 1]
    "#);

Do you remember this gadget? It was sold as letting you control your apps/games with your hands. Young-me saw this in Maplin back in the day, thinking how cool it would be. I saw one in a second-hand shop, for a price where I'm not too bothered if it's rubbish. Let's satisfy young-me's curiosity!

The box for a "Leap Motion" controller. It shows the device on the front - a light-grey metal cuboid with a piano-black top. The copy on the front of the box reads "The remarkably accurate, incredibly natural way to interact with your computer."

I've done some mad science with Ruby 4.0's new Ruby::Box namespacing feature - what if Ruby had JavaScript-style `import`/`export`, rather than requiring entire files? You only get what you import, and imported items don't "leak" between files. Try it, if you dare: github.com/AaronC81/rixby

A VS Code window with three editors:

shapes.rb:
class Rectangle export
end

class Circle export
end

assertion.rb:
export def assert_equal(expected, actual)
end

canvas_test.rb:
import { from 'canvas', :Canvas }
import { from 'shapes', :Rectangle, :Circle }
import { from 'assertion', :assert_equal }

For a bit of polish and ease-of-experimentation, there's now a basic editor! Here's one possible implementation of a rounded rectangle. Being able to define a new operation that inspects the geometry, like this `rotate_around_centre`, feels rather useful to me.

YASCAD code editor, with dark theme. On the left, a Monaco code editor, with file and rendering controls. On the right, an orange cuboid with rounded corners on a grid.

The code in the editor is:

operator rotate_around_centre(angle) {
    c = buffer() children();

    // Move so that centre is on (0,0), rotate, then move back
    dx = c.origin.x + c.size.x / 2;
    dy = c.origin.y + c.size.y / 2;
    translate([dx, dy])
    rotate(angle)
    translate([-dx, -dy])
    copy(c);
}

// Subtract this from a rect to round its closest corner to the origin
module rounded_corner(r) {
    translate([r, r]) rotate(180)
    difference() {
        square(r);
        circle(r=r);
    }
}

module rounded_rect(w, h, r) {
    difference() {
        square([w, h]);

                                                        rounded_corner(r);
        rotate_around_centre(90)  translate([w-r, 0])   rounded_corner(r);
        rotate_around_centre(180) translate([w-r, h-r]) rounded_corner(r);
        rotate_around_centre(270) translate([0, h-r])   rounded_corner(r);
    }
}

linear_extrude(1)
rounded_rect(5, 6, r=1);
Aaron Christiansen@aaronc.cc · 11mo ago

Starting to get interesting - now you can write operator modules which behave based on some properties of their children - here repeating the children in a grid. You can't do something like this in OpenSCAD unless you also pass a separately-calculated size for the children.

Starting to get interesting - now you can write operator modules which behave based on some properties of their children - here repeating the children in a grid. You can't do something like this in OpenSCAD unless you also pass a separately-calculated size for the children.

Code editor showing cylinders repeated on a 3x4 grid.

Code:

```
// Repeat the children a certain number of times in the
// X and Y directions to form a grid
operator repeat(xs, ys) {
    // Draw the children to a virtual buffer once
    t = buffer() children();

    for (x = [0:xs-1]) {
        for (y = [0:ys-1]) {
            // Based on the size of that virtual buffer,
            // stamp out copies of the children
            translate([t.size.x * x, t.size.y * y]) {
                copy(t);
            }
        }
    }
}

repeat(3, 4)
cylinder(5, 5);
```
Aaron Christiansen@aaronc.cc · 11mo ago

Having a crack at a project I've been thinking about for a while. An OpenSCAD-alike where you can introspect objects you've already drawn - which means less thinking about how to structure your variables for quick thrown-together 3D prints. First prototype works well!

A code editor showing an STL preview with a small cube on top of another big cube. The code beside is:

```
// Create an object and make a binding for it
big_cube = cube([20, 20, 20]);

// Reference that binding to create another object
translate([0, 0, big_cube.size.y])
cube([5, 5, 5]);
```

Having a crack at a project I've been thinking about for a while. An OpenSCAD-alike where you can introspect objects you've already drawn - which means less thinking about how to structure your variables for quick thrown-together 3D prints. First prototype works well!

A code editor showing an STL preview with a small cube on top of another big cube. The code beside is:

```
// Create an object and make a binding for it
big_cube = cube([20, 20, 20]);

// Reference that binding to create another object
translate([0, 0, big_cube.size.y])
cube([5, 5, 5]);
```

Seeing if I can put together a tiny macro pad to live in my work’s office - I miss my macros when I’m not working from home! First steps - a 3D-printed plate with hardwired Gateron Browns (with jumpers I now realise I’ve made far too long)…

Six Gateron Brown key switches inserted into a black plastic plate, with jumper wires coming out the back

After a few days - this works alright! Only issue is there's some disagreement between Git and Jujutsu about a few files from LFS - Git is tracking them, but Jujutsu thinks they're new. If I delete them, Git sees the repo as dirty, but Jujutsu is happy and works a treat.

Aaron Christiansen@aaronc.cc · last yr.

No support yet for submodules and LFS limits how much I can use it, sadly, but I’m keen to see whether I can just ignore all that stuff in Jujutsu and fall back to Git’s CLI instead

I don’t know how long this has existed, but I’ve never spotted it before… Apple Photos smooshing together geotagging and event listings to give concert photos an artist tag - which you can search by! - is such a damn clever feature

A screenshot of Apple Photos on iOS showing “Magdalena Bay Concert” as a tag