Tactical vs Strategic Design
Last time, I wrote some about initial design work for putting together a graph visualization library. I've made a little progress, then got stuck (mentally) on interactivity. Here's a little about that process.
The Problem
In the future, I expect this graphing library to have somewhere between "some" and "quite a few" nodes, any of which can be visible at any given time. The challenge is figuring out which, if any, are clicked on.
Solutions
Most of the libraries that I've worked with have some kind of event handler which contains information like what kind of event was generated, where and any other state data.
For example, you might get something that translates to: "The user pressed the left mouse button down 392 pixels from the left most part of the window and 107 pixels from the top." As I want this library to have interactivity, i.e., do something when you click the left mouse button, I need to be able to handle this information and correlate it with the current stuff on the screen. In my case, that stuff translates to rendered drawings of nodes and arrows, maybe with some panning behavior to enable you to look through the whole graph.
The standard way of doing this, as gathered by perusing the source code of a few pygame-based widget libraries, is to simply iterate through the various rectangular bounding boxes of objects in the scene and see if any have an intersection with the event location. As someone who dabbles with performance optimization, this struck me as an O(n) problem and therefore not fast enough.
The next thing that came to mind was to use something like a sparse quadtree that executes in something like logarithmic time, which would be much better, especially considering that I intend to eventually have some kind of pan/zoom interface and hundreds of nodes.
I hemmed and hawed about this for a bit and dug through some more source code. Someone must have already implemented this in the graphics libraries (not that I could find, per the above), but I searched anyway. I'm sure that there is a Python library for processing sparse quad trees, but I had hoped it was already integral to the widget libraries.
Then, of course, I realized that I was committing the cardinal sin of programming. I thought back on the original design constraints - developer speed, rather than anything else - and realized I was prematurely optimizing.
While switching the click/check interface from one which takes a collection of nodes (and their bounding boxes) to one which takes a quad tree (or something similar) will require some rewrite and additional state management in the future, it's still probably better than to not get there at all because of undue consideration for performance when the initial node set will probably number in the tens. In short, O(tens) is not worth worrying about when T(tens) is milliseconds at worst (and the fastest monitors I'm likely to encounter need a refresh period of about 8 milliseconds).
Conclusion
For now, I'm just going to use a linear registry (list) for this kind of stuff and treat it as semi-global state (probably encapsulated in whatever world/graph engine keeps the whole thing stitched together). I'd prefer to make it more efficient, but if there's already a function to do 'click in box' collision detection, it's worth reusing.
Thanks for reading!