## Drawing Graphs in GH2

### November 29, 2015

One of the more conspicuous ways in which working in Grasshopper differs from working in -say- Visual Studio, is that it is trivial to tweak values by hand at runtime. If one wishes to provide control over some constant used in a C# algorithm, one must either build a UI with buttons, sliders, checkboxes and whatnot, or maintain a live link to a settings file which can then be modified using notepad. The first approach requires a fair amount of work on the part of the developer, the latter is supremely user-unfriendly.

Although we collect no data on this, I would not be surprised in the least to find out that the most commonly used object in all Grasshopper files ever is the number slider. Numbers are almost always one of the required inputs of any given algorithm, and the slider provides an easy way to interactively adjust them. I do not think the implementation of sliders in Grasshopper 1.0 is perfect, but at least access to *numeric constants* works far better than the way in which GH1 provides access to *equations*. The *Graph Mapper* object is probably the most commonly used worst piece of UI garbage in the history of Grasshopper.

It doesn’t support panning or zooming, it doesn’t allow the positioning of grips outside of the domain, it doesn’t allow more than one graph at the same time, it doesn’t allow custom colours/thicknesses/dashpatterns, it doesn’t provide snapping or accurate positioning of grips; it drags the joy-of-use for the entire program down. Admittedly, there are lots of annoying buzzkills in Grasshopper, but the Graph Mapper has no excuse, there’s absolutely no reason for it to be crap, and yet here we are.

Since I’ve spend the last three years writing a lot of low-level code for Grasshopper 2.0 and I figured a good way to dip my toes back into the UI pool would be to try and design a better graph mapper.

This blog post is not about doing that. I’m only going to talk about drawing graph curves, which you may have thought was the one thing the old editor did well. Not, I’m ashamed to say, even that.

All the equation types that Grasshopper 1.0 provided (Bezier, Conic, Linear, Sinc, etc.) had three things in common:

- They could be evaluated at any location within the x-domain limits.
- They did not have discontinuities.
- They did not have sharp kinks (though Gaussian, Perlin, Sine and Sinc could be adjusted to at least have narrow spikes).

Let’s consider how breaking the above rules would affect the naïve graph drawing routine employed by Grasshopper 1.0, and then see if we can somehow improve it.

The most obvious way to draw the graph of a function is simply to evaluate that function at equally spaced intervals and then connect the dots. Drawing a polyline that connects a bunch of coordinates is trivial so the only thing we have to decide on, is the interval. Obviously, the closer together adjacent samples are, the more accurate our graph will be. On the other hand, lots of samples take lots of time to compute and we do not want the redraw frequency to drop significantly under 20~25 frames per second. Drawing several different functions into a full-screen window may well require tens of thousands of evaluations per draw event if we insist on evaluating everything at pixel accuracy. We will thus need a mechanism for throttling the sampling density, which in turn means we need to make our graphs look as good as possible with a very low number of samples.

As the above picture shows, sampling a function based on a repetitive grid can result in four major problems:

- If the function starts and stops in between samples, it will appear cut off. The visual error in this case is at most as severe as the sampling grid is coarse.
- If the function contains steep segments, they may be ‘missed’ by the sampling grid. This can potentially lead to a
*huge*loss of visual detail. - Functions with
*poles*or*asymptotes*result in unconnected segments, but the sampling grid just steamrolls over this topological curiosity. - Kinks that fall in between samples will appear blunted.

All of the above problems are the result of a sampling grid approach. If there’s a better way to approximate the actual function, then we should of course opt for that. Some functions consist solely of collections of straight line segments which can usually be converted into display geometry very efficiently and with a minimal loss of accuracy.

Take for example the *block* or *nearest-neighbour* interpolator. It contains only horizontal and vertical segments which are easy to compute. Throwing a sampling grid at such a graph will result in a jittery graph with slanted rather than vertical segments:

But we aren’t always so lucky as to be able to compute accurate geometry, and in many cases we do have to resort to sampling the function. Given an initial sampling grid, we may of course choose to insert additional samples at locations where we expect to be rewarded for our efforts. Ideally these locations are known in advance, but it could be we must postpone the insertion until after the initial sampling has taken place.

One place where it almost certainly pays to increase our density is between valid and invalid samples. We know for sure the function begins to exist somewhere between these two samples, but by looking more closely we can definitely narrow down the exact location. The *cubic interpolator* for example is only well defined *between* grips, not before the first or after the last one. It is after all an interpolator, not an extrapolator. This means the function only exists in the domain (Xa, Xb) where Xa is the lowest x-coordinate of all grips and Xb the highest. Using a naïve sampling grid which does not take the limits of the function into account, the drawn graph appears inconsistent even during panning.

The blunting of kinks is actually a very similar problem with a very similar solution. Instead of adding more samples to the edges of the function domain, we need to add samples to the interior where we expect to find kinks. Locations of grips are good places to expect a higher than average probability of kinks, and in any case we’d like the approximation of the graph to be as exact as possible at grip locations, lest they appear some distance away from the actual graph.

The log-linear interpolator is an example of a function with frequent kinks. Every time a grip is surrounded by grips that are significantly lower, a sharp kink will appear:

Finally (and most troublesome) a better way is needed to deal with poles. This is not just a matter of adjusting some sample locations, we must be able to draw the graph all the way to positive/negative infinity on either side of the pole, *and* we must take care to not connect the samples on either side, otherwise we give the impression the function actually exists where it in fact doesn’t.

Bulirsch-Stoer interpolation is a rational interpolation algorithm (as opposed to the more common polynomial algorithms) which has fallen out of favour since Michael Floater and Kai Hormann came up with an improved algorithm which doesn’t generate poles, but it does present a wonderful test case for graphing UI (both algorithms are available in Grasshopper 2.0 btw. along with Equidistant Polynomial, Neville Polynomial, Cubic, Akima, Log-Linear etc.). Behold the absolute foulness that is a function with poles imposed on a fixed sampling grid, as opposed to a grid that has been split into distinct sections with appropriate density distributions:

November 30, 2015 at 16:16

C’mon, path mapper is quite good I would say. I use it very often and don’t complain

December 1, 2015 at 14:21

@olkus well… not complaining about something is not a good way to improve it :)

November 30, 2015 at 23:38

I would rather point out the News: the article talks about gh2!

December 1, 2015 at 02:59

Awesome work David, any idea when this will be available?

December 1, 2015 at 14:24

@charles, not any time soon. this is the first serious bit of user-interface in GH2, the colour pickers I worked on earlier were just to get comfortable with Eto. What I’m hoping to accomplish with this is to find and solve 90% of common UI problems I’m going to run into anyway so that future work will go smoother. But even so it will take many months to get a basic working canvas, and that’s not even to mention the hundreds upon hundreds of components I’ll have to write.

December 1, 2015 at 15:35

Interesting article David! I particularly like the UI/Usage graph. One of the first questions my students ask me when teaching graph mapper is how to control grab points accurately. Glad to see you are on it!