kplot icon indicating copy to clipboard operation
kplot copied to clipboard

some patches

Open jcupitt opened this issue 1 year ago • 5 comments

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

image

  1. 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.

  2. 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).

jcupitt avatar Nov 19 '24 12:11 jcupitt

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?

kristapsdz avatar Feb 09 '25 22:02 kristapsdz

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.

Image

Setting an interval rather than the position makes it easy to change tic density in a nice way as windows are scaled, for example:

Image

You can see that the Y axis tics change in density (but don't move!) as the window is resized.

jcupitt avatar Feb 10 '25 23:02 jcupitt

Excellent point. I'll start adding that in! It might look a little different in terms of API, but not much.

kristapsdz avatar Feb 16 '25 21:02 kristapsdz

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.

jcupitt avatar Feb 25 '25 19:02 jcupitt

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.

jcupitt avatar Feb 25 '25 19:02 jcupitt