Bug report: svg based patch grid transform fails

TL;DR I’m porting a sample agent based model to D3 and Observable.

The Observable patches transform appears to fail. The left is the JavaScript/D3 model, the right is Observable:


The agent based model does not have a view, it is the Model of Model/View, where the View is any of several renderers.

Here is printing out data, and with a 3D & D3 View:

The hello Views paint the patches random grays, creates 10 turtles and randomly links them together.

While beginning the D3 port to Observable, I found difficulties with the patches.

… there appears to be a problem with the transform into “patch coordinates”.

Here’s the same JavaScript/D3 code, simplified to have the D3 view with only the patches drawn, matching the notebook.
… with this code:

Is this a bug? Or am I making a silly D3/Observable mistake?

The main thing that’s missing is a minus sign.

Instead of

translate(${model.world.minXcor}, ${-model.world.maxYcor})

You want

translate(${-model.world.minXcor}, ${-model.world.maxYcor})

That said, the way this notebook is setup, it’s very difficult to develop in Observable because it uses mutable state extensively: each cell modifies the SVG element returned by the svg node rather than creating new (“pure”) DOM. Give me a moment to refactor it to make it a little easier to debug.

Here’s a version with minimal refactoring that works in the Observable style. Instead of a cell having a side-effect where it modifies the SVG element defined by svg or g, each cell is a function that is passed the element you want to modify. So, if you change the function definition, the entire SVG is thrown away and re-run, and you get the expected result.


Hey Owen,

Following Mike’s lead — here’s a version with additional appendLines() and appendTurtles() functions defined, as well as a draw loop that calls model.step() and yields the SVG node.

Good luck!


God these are brilliant! I can’t thank you enough for the help. But I can try … THANKS!

1 Like

Hey Jeremy, a small simplification is possible in your implementation: there’s no need to join .data(model.links) and .data(model.turtles) in the draw() step, as the binding is made directly with the turtles :wink:


Thanks Fil.

There is one last oddity: one of the turtles does not draw! Probably the first or last, haven’t tested for that yet. The links connecting to the “invisible” turtle work fine so it’s “there” but just not drawn.

Set your override’s number of turtles to 2 and it will be obvious.


the reason is that the append('defs')… classes polygon as .dart; then, the data join being made on selectAll('.dart'), the first turtle is joined with this defs>g>polygon and you get n-1 use

1 Like