some patches
I've used kplot for my project and added a couple of features, if anyone is interested.
https://github.com/jcupitt/nip4/tree/main/src/kplot
-
Better tic positioning. kplot often makes plots with tics labelled with unintuitive values, like 0.384756345, for example. I removed the xtic/ytic config options and switched it to xinterval/yinterval. Applications can set these to (for example) 0.05 and kplot will position and label tics with 0.05 intervals, making charts much simpler to interpret.
-
Basic hit support. I needed to be able to display values under the mouse, so I exposed kplotctx in the public API and added something to transform from cairo coordinates to plot indexes.
Please feel free to adapt any code in my kplot/ directory (though the rest of the code is GPL).
Thank you for this! I've merged a form of the second one ("basic hit support"). I'm curious why you ended up adding the interval support instead of using the callbacks to format the tic value in a more pleasing way?
I think (I hope I have this right!) the callbacks only let you set the string for the tic, not the tic position. I often have axes which don't start at 0, so I need tics with an offset. Plus this is a scientific application, so tics need to be exactly on the labelled position.
Setting an interval rather than the position makes it easy to change tic density in a nice way as windows are scaled, for example:
You can see that the Y axis tics change in density (but don't move!) as the window is resized.
Excellent point. I'll start adding that in! It might look a little different in terms of API, but not much.
I don't know if this is worth considering, but nip4 uses this code to pick xinterval / yinterval:
static double
drawstate_pick_interval(double min, double max, int size)
{
double range = max - min;
double magnitude = pow(10.0, ceil(log10(range)));
if (size < 200)
return magnitude / 2.0;
else if (size < 400)
return magnitude / 5.0;
else if (size < 600)
return magnitude / 10.0;
else if (size < 1400)
return magnitude / 20.0;
else if (size < 2000)
return magnitude / 50.0;
else
return magnitude / 100.0;
}
static void
drawstate_draw(Drawstate *state,
cairo_t *cr, int width, int height, gboolean thumbnail, Plot *plot)
{
// white background
GdkRGBA white;
gdk_rgba_parse(&white, "white");
gdk_cairo_set_source_rgba(cr, &white);
cairo_rectangle(cr, 0.0, 0.0, width, height);
cairo_fill(cr);
if (thumbnail) {
state->kplot->cfg.xinterval = 0.0;
state->kplot->cfg.yinterval = 0.0;
}
else {
state->kplot->cfg.xinterval =
drawstate_pick_interval(plot->xmin, plot->xmax, width);
state->kplot->cfg.yinterval =
drawstate_pick_interval(plot->ymin, plot->ymax, height);
}
// and plot
kplot_draw_ctx(&state->kctx, state->kplot, width, height, cr);
}
So tics are in steps of 1, 2, 5 or 10 (ie. 1 again), with the magnitude being the log10 of the range that needs to be covered. It seems to work well!
The magic numbers 200, 400, 600 etc. in drawstate_pick_interval() are related to the font size and should really be calculated at runtime, but I was too lazy.
Sorry, one more thing, I swapped the BSD reallocarray for the one from musl:
https://github.com/jcupitt/nip4/blob/main/src/kplot/reallocarray.c
it's a little nicer.