Example #1
0
static void
filter(GwyGraph *graph)
{
    GwyContainer *data;
    GwyGraphCurveModel *cmodel, *cmodelnew;
    GwyGraphModel *model;
    const gdouble *xdata, *ydata;
    gdouble *newydata;
    gint i, ncurves, ndata;
    GQuark quark;

    gwy_app_data_browser_get_current(GWY_APP_CONTAINER, &data,
                                     GWY_APP_GRAPH_MODEL_KEY, &quark,
                                     0);
    gwy_app_undo_qcheckpointv(data, 1, &quark);

    model = gwy_graph_get_model(graph);
    ncurves = gwy_graph_model_get_n_curves(model);
    for (i = 0; i < ncurves; i++) {
        cmodel = gwy_graph_model_get_curve(model, i);
        cmodelnew = gwy_graph_curve_model_new_alike(cmodel);
        xdata = gwy_graph_curve_model_get_xdata(cmodel);
        ydata = gwy_graph_curve_model_get_ydata(cmodel);
        ndata = gwy_graph_curve_model_get_ndata(cmodel);
        newydata = g_new(gdouble, ndata);
        filter_do(ydata, newydata, ndata);
        gwy_graph_curve_model_set_data(cmodelnew, xdata,
                                       newydata, ndata);
        g_free(newydata);
        gwy_graph_model_remove_curve(gwy_graph_get_model(graph), i);
        gwy_graph_model_add_curve(model, cmodelnew);
        g_object_unref(cmodelnew);
    }
}
Example #2
0
static void
prof_execute(ProfControls *controls,
             GwyContainer *data)
{
    GwyGraphCurveModel *gcmodel;
    GwyGraphModel *gmodel;
    ProfArgs *args = controls->args;
    gchar *s;
    gint i, n;

    n = gwy_selection_get_data(controls->selection, NULL);
    g_return_if_fail(n);

    g_object_set(controls->gmodel, "label-visible", TRUE, NULL);
    if (!args->separate) {
        gwy_app_add_graph_or_curves(controls->gmodel,
                                    data, &args->target_graph, 1);
        return;
    }

    for (i = 0; i < n; i++) {
        gmodel = gwy_graph_model_new_alike(controls->gmodel);
        gcmodel = gwy_graph_model_get_curve(controls->gmodel, i);
        gcmodel = gwy_graph_curve_model_duplicate(gcmodel);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
        g_object_get(gcmodel, "description", &s, NULL);
        g_object_set(gmodel, "title", s, NULL);
        g_free(s);
        gwy_app_data_browser_add_graph_model(gmodel, data, TRUE);
        g_object_unref(gmodel);
    }
}
Example #3
0
static void
prof_update_curve(ProfControls *controls,
                  gint i)
{
    GwyGraphCurveModel *gcmodel;
    gdouble xy[4], h;
    gint xl0, yl0, xl1, yl1;
    gint n, lineres;
    gchar *desc;

    g_return_if_fail(gwy_selection_get_object(controls->selection, i, xy));

    /* The ω=0 pixel is always at res/2, for even dimensions it means it is
     * shifted half-a-pixel to the right from the precise centre. */
    xl0 = gwy_data_field_get_xres(controls->psdffield)/2;
    yl0 = gwy_data_field_get_yres(controls->psdffield)/2;
    xl1 = floor(gwy_data_field_rtoj(controls->psdffield, xy[0]));
    yl1 = floor(gwy_data_field_rtoi(controls->psdffield, xy[1]));
    xy[0] += gwy_data_field_get_xoffset(controls->psdffield);
    xy[1] += gwy_data_field_get_yoffset(controls->psdffield);
    h = hypot(controls->hx*xy[0], controls->hy*xy[1])/hypot(xy[0], xy[1]);

    if (!controls->args->fixres) {
        lineres = GWY_ROUND(hypot(abs(xl0 - xl1) + 1, abs(yl0 - yl1) + 1));
        lineres = MAX(lineres, MIN_RESOLUTION);
    }
    else
        lineres = controls->args->resolution;

    gwy_data_field_get_profile(controls->psdffield, controls->line,
                               xl0, yl0, xl1, yl1,
                               lineres,
                               1,
                               controls->args->interpolation);
    gwy_data_line_multiply(controls->line, h);

    n = gwy_graph_model_get_n_curves(controls->gmodel);
    if (i < n) {
        gcmodel = gwy_graph_model_get_curve(controls->gmodel, i);
    }
    else {
        gcmodel = gwy_graph_curve_model_new();
        g_object_set(gcmodel,
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "color", gwy_graph_get_preset_color(i),
                     NULL);
        gwy_graph_model_add_curve(controls->gmodel, gcmodel);
        g_object_unref(gcmodel);
    }

    gwy_graph_curve_model_set_data_from_dataline(gcmodel, controls->line, 0, 0);
    desc = g_strdup_printf(_("PSDF %.0f°"), 180.0/G_PI*atan2(-xy[1], xy[0]));
    g_object_set(gcmodel, "description", desc, NULL);
    g_free(desc);
}
Example #4
0
static GwyGraphModel*
create_graph_model(const GwyXY *points,
                   const gdouble *xdata, gdouble *ydata, guint n,
                   gboolean x, gboolean y)
{
    GwyGraphModel *gmodel = gwy_graph_model_new();
    GwyGraphCurveModel *gcmodel;
    guint i;

    if (!x && !y)
        return NULL;

    if (x) {
        gcmodel = gwy_graph_curve_model_new();
        for (i = 0; i < n; i++)
            ydata[i] = points[i].x;
        gwy_graph_curve_model_set_data(gcmodel, xdata, ydata, n);
        g_object_set(gcmodel,
                     "description", "X",
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "color", gwy_graph_get_preset_color(0),
                     NULL);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
    }

    if (y) {
        gcmodel = gwy_graph_curve_model_new();
        for (i = 0; i < n; i++)
            ydata[i] = points[i].y;
        gwy_graph_curve_model_set_data(gcmodel, xdata, ydata, n);
        g_object_set(gcmodel,
                     "description", "Y",
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "color", gwy_graph_get_preset_color(1),
                     NULL);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
    }

    return gmodel;
}
static GwyGraphModel*
rhk_sm4_page_to_graph_model(const RHKPage *page)
{
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *gcmodel;
    GwySIUnit *siunit;
    const gint32 *pdata;
    const gchar *name;
    gint res, ncurves, i, j;
    gdouble *xdata, *ydata;

    res = page->x_size;
    ncurves = page->y_size;

    gmodel = gwy_graph_model_new();
    pdata = (const gint32*)page->data;
    xdata = g_new(gdouble, res);
    ydata = g_new(gdouble, res);
    name = page->strings[RHK_STRING_LABEL];
    for (i = 0; i < ncurves; i++) {
        gcmodel = gwy_graph_curve_model_new();
        for (j = 0; j < res; j++) {
            xdata[j] = j*page->x_scale + page->x_offset;
            ydata[j] = (GINT32_FROM_LE(pdata[i*res + j])*page->z_scale
                        + page->z_offset);
        }
        gwy_graph_curve_model_set_data(gcmodel, xdata, ydata, res);
        g_object_set(gcmodel,
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "color", gwy_graph_get_preset_color(i),
                     NULL);
        if (name)
            g_object_set(gcmodel, "description", name, NULL);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
    }
    g_free(ydata);
    g_free(xdata);

    /* Units */
    siunit = gwy_si_unit_new(page->strings[RHK_STRING_X_UNITS]);
    g_object_set(gmodel, "si-unit-x", siunit, NULL);
    g_object_unref(siunit);

    siunit = gwy_si_unit_new(page->strings[RHK_STRING_Z_UNITS]);
    g_object_set(gmodel, "si-unit-y", siunit, NULL);
    g_object_unref(siunit);

    if (name)
        g_object_set(gmodel, "title", name, NULL);

    return gmodel;
}
Example #6
0
static GwyGraphModel*
spectra_to_graph(GwySpectra *spectra)
{
    GwyGraphModel *gmodel;
    const gchar* graph_title;
    GwyGraphCurveModel *cmodel;
    gchar *curve_title = NULL;
    guint j, k, n_spectra, n_points;
    GwyDataLine *dline;
    gdouble *data, *xdata, *ydata, x_offset, x_realsize;
    GwySIUnit *x_si_unit, *y_si_unit;

    if (!(n_spectra = gwy_spectra_get_n_spectra(spectra))) {
        gwy_debug("rhk-spm32: no spectra in rhkpage - something is odd\n");
        return NULL;
    }
    dline = gwy_spectra_get_spectrum(spectra, 0);
    n_points = gwy_data_line_get_res(dline);
    x_si_unit = gwy_data_line_get_si_unit_x(dline);
    y_si_unit = gwy_data_line_get_si_unit_y(dline);
    xdata = g_new0(gdouble, n_points);
    ydata = g_new0(gdouble, n_points);
    x_offset = gwy_data_line_get_offset(dline);
    x_realsize = gwy_data_line_get_real(dline);
    for (j = 0; j < n_points; j++)
        xdata[j] = x_offset+j*x_realsize;
    gmodel = gwy_graph_model_new();
    g_object_set(gmodel, "si-unit-x", x_si_unit, "si-unit-y", y_si_unit, NULL);
    graph_title = gwy_spectra_get_title(spectra);
    g_object_set(gmodel, "title", graph_title, NULL);
    // tends to obstruct the curves - if there are more than a few - not
    // good - makes it hard to grab curves?
    //g_object_set(gmodel, "label-visible", FALSE, NULL);
    for (k = 1; k <= n_spectra; k++) {
        dline = gwy_spectra_get_spectrum(spectra, k-1);
        data = gwy_data_line_get_data(dline);
        for (j = 0; j < n_points; j++)
            ydata[j] = data[j];
        cmodel = gwy_graph_curve_model_new();
        gwy_graph_model_add_curve(gmodel, cmodel);
        g_object_unref(cmodel);
        curve_title = g_strdup_printf("%s %d", graph_title, k);
        g_object_set(cmodel, "description", curve_title,
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "color", gwy_graph_get_preset_color(k),
                     NULL);
        gwy_graph_curve_model_set_data(cmodel, xdata, ydata, n_points);
    }
    g_free(ydata);
    g_free(xdata);

    return gmodel;
}
Example #7
0
static void
fit_plot_curve(FitArgs *args)
{
    GwyGraphCurveModel *cmodel;
    gdouble *xd, *yd;
    gboolean initial, ok;   /* XXX: ignored */
    gint i, n;
    gdouble *param;

    if (!args->is_fitted && !args->is_estimated)
        return;

    initial = !args->is_fitted;
    n = gwy_nlfit_preset_get_nparams(args->fitfunc);
    param = g_newa(gdouble, n);
    for (i = 0; i < n; i++) {
        FitParamArg *arg;

        arg = &g_array_index(args->param, FitParamArg, i);
        param[i] = initial ? arg->init : arg->value;
    }

    n = gwy_data_line_get_res(args->xdata);
    g_return_if_fail(n == gwy_data_line_get_res(args->ydata));
    xd = gwy_data_line_get_data(args->xdata);
    yd = gwy_data_line_get_data(args->ydata);

    for (i = 0; i < n; i++)
        yd[i] = gwy_nlfit_preset_get_value(args->fitfunc, xd[i], param, &ok);

    if (gwy_graph_model_get_n_curves(args->graph_model) == 2)
        cmodel = gwy_graph_model_get_curve(args->graph_model, 1);
    else {
        cmodel = gwy_graph_curve_model_new();
        g_object_set(cmodel,
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "color", &args->fitcolor,
                     NULL);
        gwy_graph_model_add_curve(args->graph_model, cmodel);
        g_object_unref(cmodel);
    }
    g_object_set(cmodel,
                 "description",
                 initial
                 ? gwy_sgettext("Estimate")
                 : gwy_sgettext("Fit"),
                 NULL);
    gwy_graph_curve_model_set_data(cmodel, xd, yd, n);
}
Example #8
0
static GwyGraphModel*
pt3file_extract_decay(const PicoHarpFile *pt3file,
                      const guchar *buf)
{
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *gcmodel;
    GwySIUnit *siunit;
    gdouble *xdata, *ydata;
    guint maxtime, n, i;
    const guchar *p;

    n = pt3file->number_of_records;

    maxtime = 0;
    p = buf;
    for (i = 0; i < n; i++) {
        PicoHarpT3Record rec;

        p = read_t3_record(&rec, p);
        if (rec.channel != 15 && rec.time > maxtime)
            maxtime = rec.time;
    }

    xdata = g_new(gdouble, maxtime+1);
    for (i = 0; i <= maxtime; i++)
        xdata[i] = 1e-9*i*pt3file->board.resolution;
    ydata = g_new0(gdouble, maxtime+1);

    p = buf;
    for (i = 0; i < n; i++) {
        PicoHarpT3Record rec;

        p = read_t3_record(&rec, p);
        if (rec.channel != 15)
            ydata[rec.time] += 1.0;
    }

    gcmodel = gwy_graph_curve_model_new();
    g_object_set(gcmodel,
                 "mode", GWY_GRAPH_CURVE_LINE,
                 "description", "Fluorescence decay",
                 NULL);
    gwy_graph_curve_model_set_data(gcmodel, xdata, ydata, maxtime+1);
    g_free(ydata);
    g_free(xdata);

    siunit = gwy_si_unit_new("s");

    gmodel = gwy_graph_model_new();
    g_object_set(gmodel,
                 "title", "Fluorescence decay",
                 "si-unit-x", siunit,
                 "axis-label-bottom", "time",
                 "axis-label-left", "count",
                 NULL);
    gwy_graph_model_add_curve(gmodel, gcmodel);
    g_object_unref(gcmodel);
    g_object_unref(siunit);

    return gmodel;
}
Example #9
0
static void
preview(EntropyControls *controls)
{
    EntropyArgs *args = controls->args;
    GwyDataField *dfield = controls->dfield;
    GwyDataField *mfield = controls->mfield;
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *gcmodel;
    GwyDataLine *ecurve;
    gchar buf[24];
    gdouble S, s, Smax = 0.0;

    ecurve = gwy_data_line_new(1, 1.0, FALSE);
    if (args->mode == ENTROPY_VALUES) {
        S = gwy_data_field_area_get_entropy_at_scales(dfield, ecurve,
                                                      mfield, args->masking,
                                                      0, 0,
                                                      dfield->xres,
                                                      dfield->yres,
                                                      0);
        s = gwy_data_field_area_get_rms_mask(dfield,
                                             mfield, args->masking,
                                             0, 0, dfield->xres, dfield->yres);
        Smax = ENTROPY_NORMAL + log(s);
    }
    else {
        GwyDataField *xder = gwy_data_field_new_alike(dfield, FALSE);
        GwyDataField *yder = gwy_data_field_new_alike(dfield, FALSE);

        compute_slopes(controls->dfield,
                       args->fit_plane ? args->kernel_size : 0, xder, yder);
        xder = fake_mask(xder, mfield, args->masking);
        yder = fake_mask(yder, mfield, args->masking);
        if (args->mode == ENTROPY_ANGLES)
            transform_to_sphere(xder, yder);

        S = gwy_data_field_get_entropy_2d_at_scales(xder, yder, ecurve, 0);
        if (args->mode == ENTROPY_SLOPES) {
            s = calculate_sigma2_2d(xder, yder);
            Smax = ENTROPY_NORMAL_2D + log(s);
        }

        g_object_unref(xder);
        g_object_unref(yder);
    }

    g_snprintf(buf, sizeof(buf), "%g", S);
    gtk_label_set_text(GTK_LABEL(controls->entropy), buf);

    if (args->mode != ENTROPY_ANGLES) {
        g_snprintf(buf, sizeof(buf), "%g", Smax - S);
        gtk_label_set_text(GTK_LABEL(controls->entropydef), buf);
    }
    else
        gtk_label_set_text(GTK_LABEL(controls->entropydef), _("N.A."));

    gmodel = gwy_graph_get_model(GWY_GRAPH(controls->graph));
    gwy_graph_model_remove_all_curves(gmodel);
    g_object_set(gmodel,
                 "axis-label-bottom", "log h",
                 "axis-label-left", "S",
                 "label-position", GWY_GRAPH_LABEL_NORTHWEST,
                 NULL);

    if (gwy_data_line_get_min(ecurve) > -0.5*G_MAXDOUBLE) {
        gcmodel = gwy_graph_curve_model_new();
        g_object_set(gcmodel,
                     "description", _("Entropy at scales"),
                     "mode", GWY_GRAPH_CURVE_LINE_POINTS,
                     "color", gwy_graph_get_preset_color(0),
                     NULL);
        gwy_graph_curve_model_set_data_from_dataline(gcmodel, ecurve, 0, 0);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
    }

    if (S > -0.5*G_MAXDOUBLE) {
        GwyDataLine *best = gwy_data_line_duplicate(ecurve);
        gdouble *ydata = gwy_data_line_get_data(best);
        guint i, res = gwy_data_line_get_res(best);

        for (i = 0; i < res; i++)
            ydata[i] = S;

        gcmodel = gwy_graph_curve_model_new();
        g_object_set(gcmodel,
                     "description", _("Best estimate"),
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "color", gwy_graph_get_preset_color(1),
                     NULL);
        gwy_graph_curve_model_set_data_from_dataline(gcmodel, best, 0, 0);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
        g_object_unref(best);
    }

    g_object_unref(ecurve);

    zoom_in_changed(controls, GTK_TOGGLE_BUTTON(controls->zoom_in));
}
Example #10
0
static gboolean
read_aist_curve(const guchar **p, gsize *size, AistContext *context)
{
    AistCurve curve;
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *gcmodel;
    GwySIUnit *xunit, *yunit;
    gboolean ok = FALSE;
    guint len, viewlen, i;
    const guchar *data, *viewdata;
    const gdouble *xdata, *ydata;
    gdouble *xdatacal, *ydatacal;
    gdouble *must_free = NULL;
    gdouble qx, qy;
    GQuark quark;

    gwy_clear(&curve, 1);

    gwy_debug("reading common");
    if (!read_aist_common(p, size, &curve.common))
        goto fail;

    gwy_debug("reading curve");
    if (!read_qt_int(p, size, &curve.res))
        goto fail;

    if (!read_qt_byte_array(p, size, &len, &data))
        goto fail;
    if (len != 2*curve.res*sizeof(gdouble))
        goto fail;

    /* Again something called view data.  Skip it.  The units follow. */
    if (!read_qt_byte_array(p, size, &viewlen, &viewdata))
        goto fail;

    if (!read_qt_string(p, size, &curve.xunits)
        || !read_qt_string(p, size, &curve.yunits))
        goto fail;

    xunit = extract_units(curve.xunits, &qx);
    yunit = extract_units(curve.yunits, &qy);

    /* The data are already stored as doubles in the correct order, so save
     * work if also the endianess matches. */
    if (G_BYTE_ORDER == G_BIG_ENDIAN) {
        must_free = g_new(gdouble, 2*curve.res);
        xdata = must_free;
        ydata = xdata + curve.res;
        gwy_memcpy_byte_swap(data, (guchar*)must_free, 8, 2*curve.res, 7);
    }
    else if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
        xdata = (const gdouble *)data;
        ydata = xdata + curve.res;
    }

    xdatacal = g_new(gdouble, curve.res);
    ydatacal = g_new(gdouble, curve.res);
    for (i = 0; i < curve.res; i++) {
        xdatacal[i] = xdata[i]*qx;
        ydatacal[i] = ydata[i]*qy;
    }

    gcmodel = gwy_graph_curve_model_new();
    gwy_graph_curve_model_set_data(gcmodel, xdatacal, ydatacal, curve.res);
    g_object_set(gcmodel,
                 "mode", GWY_GRAPH_CURVE_LINE,
                 "description", curve.common.description,
                 NULL);
    g_free(must_free);
    g_free(xdatacal);
    g_free(ydatacal);

    gmodel = gwy_graph_model_new();
    gwy_graph_model_add_curve(gmodel, gcmodel);
    g_object_unref(gcmodel);
    g_object_set(gmodel,
                 "title", curve.common.name,
                 "si-unit-x", xunit,
                 "si-unit-y", yunit,
                 NULL);
    g_object_unref(xunit);
    g_object_unref(yunit);

    quark = gwy_app_get_graph_key_for_id(context->graph_id+1);
    gwy_container_set_object(context->container, quark, gmodel);
    g_object_unref(gmodel);

    context->graph_id++;

    ok = TRUE;

fail:
    free_aist_common(&curve.common);
    g_free(curve.xunits);
    g_free(curve.yunits);
    return ok;
}
Example #11
0
static void
create_profiles(const X3PFile *x3pfile,
                GwyContainer *container)
{
    GwyGraphModel *gmodel;
    GwySIUnit *siunitx, *siunity;
    GArray *validx, *validy;
    GQuark quark;
    gint id;

    gmodel = gwy_graph_model_new();
    siunitx = gwy_si_unit_new("m");
    siunity = gwy_si_unit_new("m");
    g_object_set(gmodel,
                 "title", "Profiles",
                 "si-unit-x", siunitx,
                 "si-unit-y", siunity,
                 NULL);
    g_object_unref(siunity);
    g_object_unref(siunitx);

    validx = g_array_new(FALSE, FALSE, sizeof(gdouble));
    validy = g_array_new(FALSE, FALSE, sizeof(gdouble));
    for (id = 0; id < x3pfile->zres; id++) {
        guint n = x3pfile->xres;
        GwyGraphCurveModel *gcmodel;
        gchar *title;
        guint j;

        g_array_set_size(validx, 0);
        g_array_set_size(validy, 0);
        for (j = 0; j < x3pfile->xres; j++) {
            gdouble v = x3pfile->values[id*n + j];

            if (gwy_isnan(v) || gwy_isinf(v) || !x3pfile->valid[id*n + j])
                continue;

            g_array_append_val(validy, v);
            v = j*x3pfile->dx;
            g_array_append_val(validx, v);
        }

        if (!validx->len)
            continue;

        gcmodel = gwy_graph_curve_model_new();
        title = g_strdup_printf("Profile %u", id+1);
        g_object_set(gcmodel,
                     "mode", GWY_GRAPH_CURVE_LINE,
                     "description", title,
                     "color", gwy_graph_get_preset_color(id),
                     NULL);
        g_free(title);
        gwy_graph_curve_model_set_data(gcmodel,
                                       (gdouble*)validx->data,
                                       (gdouble*)validy->data,
                                       validx->len);
        gwy_graph_model_add_curve(gmodel, gcmodel);
        g_object_unref(gcmodel);
    }

    g_array_free(validy, TRUE);
    g_array_free(validx, TRUE);

    quark = gwy_app_get_graph_key_for_id(0);
    gwy_container_set_object(container, quark, gmodel);
    g_object_unref(gmodel);
}
Example #12
0
static GwyGraphModel*
create_corr_graph(GrainCrossArgs *args,
                  GwyDataField *dfield)
{
    GwyGraphCurveModel *cmodel;
    GwyGraphModel *gmodel;
    GwyGrainValue *gvalues[2];
    gdouble *xdata, *ydata, *bothdata, *rdata[2];
    GwySIUnit *siunitxy, *siunitz, *siunitx, *siunity;
    const gchar *title;
    gint i;

    gvalues[0] = gwy_grain_values_get_grain_value(args->abscissa);
    gvalues[1] = gwy_grain_values_get_grain_value(args->ordinate);

    bothdata = g_new(gdouble, 4*args->ngrains + 2);
    rdata[0] = xdata = bothdata + 2*args->ngrains;
    rdata[1] = ydata = bothdata + 3*args->ngrains + 1;
    gwy_grain_values_calculate(2, gvalues, rdata, dfield,
                               args->ngrains, args->grains);

    for (i = 0; i < args->ngrains; i++) {
        bothdata[2*i + 0] = xdata[i+1];
        bothdata[2*i + 1] = ydata[i+1];
    }
    qsort(bothdata, args->ngrains, 2*sizeof(gdouble), compare_doubles);
    for (i = 0; i < args->ngrains; i++) {
        xdata[i] = bothdata[2*i + 0];
        ydata[i] = bothdata[2*i + 1];
    }

    gmodel = gwy_graph_model_new();
    cmodel = gwy_graph_curve_model_new();
    gwy_graph_model_add_curve(gmodel, cmodel);
    g_object_unref(cmodel);

    siunitxy = gwy_data_field_get_si_unit_xy(dfield);
    siunitz = gwy_data_field_get_si_unit_z(dfield);
    siunitx = gwy_si_unit_power_multiply
                            (siunitxy, gwy_grain_value_get_power_xy(gvalues[0]),
                             siunitz, gwy_grain_value_get_power_z(gvalues[0]),
                             NULL);
    siunity = gwy_si_unit_power_multiply
                            (siunitxy, gwy_grain_value_get_power_xy(gvalues[1]),
                             siunitz, gwy_grain_value_get_power_z(gvalues[1]),
                             NULL);
    /* FIXME */
    title = gettext(gwy_resource_get_name(GWY_RESOURCE(gvalues[1])));
    g_object_set
        (gmodel,
         "title", title,
         "axis-label-left", gwy_grain_value_get_symbol_markup(gvalues[1]),
         "axis-label-bottom", gwy_grain_value_get_symbol_markup(gvalues[0]),
         "si-unit-x", siunitx,
         "si-unit-y", siunity,
         NULL);
    g_object_unref(siunitx);
    g_object_unref(siunity);

    g_object_set(cmodel,
                 "description", title,
                 "mode", GWY_GRAPH_CURVE_POINTS,
                 NULL);
    gwy_graph_curve_model_set_data(cmodel, xdata, ydata, args->ngrains);
    g_free(bothdata);

    return gmodel;
}
Example #13
0
static GwyGraphModel*
sensofar_read_profile(SensofarDataDesc *data_desc,
                      const guchar **p,
                      gsize size,
                      GError **error)
{
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *gcmodel;
    guint xres, yres, j, n;
    GwySIUnit *units = NULL;
    gdouble *xdata, *ydata;
    gdouble dx;

    yres = gwy_get_guint32_le(p);
    if (yres != 1)
        g_warning("ysize is not 1 for profile");
    xres = gwy_get_guint32_le(p);
    gwy_debug("Data size: %dx%d", xres, yres);
    if (err_SIZE_MISMATCH(error, xres*yres*sizeof(gfloat),
                          size - 2*sizeof(guint32), FALSE))
        return NULL;
    if (err_DIMENSION(error, xres) || err_DIMENSION(error, yres))
        return NULL;
    if (!((data_desc->axes_config.mppx
           = fabs(data_desc->axes_config.mppx)) > 0)) {
        g_warning("Real x size is 0.0, fixing to 1.0");
        data_desc->axes_config.mppx = 1.0;
    }

    xdata = g_new(gdouble, xres);
    ydata = g_new(gdouble, xres);
    dx = data_desc->axes_config.mppx * Micrometer;

    for (j = n = 0; j < xres; j++) {
        gdouble v = gwy_get_gfloat_le(p);
        if (v != 1000001.0) {
            xdata[n] = dx*j;
            ydata[n] = v*Micrometer;
            n++;
        }
    }

    if (!n) {
        g_free(xdata);
        g_free(ydata);
        err_NO_DATA(error);
        return NULL;
    }

    gmodel = gwy_graph_model_new();
    g_object_set(gmodel, "title", _("Profile"), NULL);

    units = gwy_si_unit_new("m"); // values are in um only
    g_object_set(gmodel, "si-unit-x", units, NULL);
    g_object_unref(units);

    units = gwy_si_unit_new("m"); // values are in um only
    g_object_set(gmodel, "si-unit-y", units, NULL);
    g_object_unref(units);

    gcmodel = gwy_graph_curve_model_new();
    gwy_graph_curve_model_set_data(gcmodel, xdata, ydata, n);
    g_object_set(gcmodel,
                 "mode", GWY_GRAPH_CURVE_LINE,
                 "description", _("Profile"),
                 NULL);
    gwy_graph_model_add_curve(gmodel, gcmodel);
    g_object_unref(gcmodel);

    return gmodel;
}
Example #14
0
static void
fit_dialog(FitArgs *args)
{
    GtkWidget *label, *dialog, *hbox, *hbox2, *table, *align, *expander, *scroll;
    GtkTable *table2;
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *cmodel;
    GwyGraphArea *area;
    GwySelection *selection;
    GwySIUnit *siunit;
    FitControls controls;
    gint response, row;
    GString *report;
    gdouble xmin, xmax;

    controls.args = args;
    controls.in_update = TRUE;
    controls.param = g_array_new(FALSE, TRUE, sizeof(FitParamControl));

    gmodel = gwy_graph_get_model(GWY_GRAPH(args->parent_graph));
    gwy_graph_model_get_x_range(gmodel, &xmin, &xmax);
    g_object_get(gmodel, "si-unit-x", &siunit, NULL);
    args->abscissa_vf
        = gwy_si_unit_get_format_with_digits(siunit,
                                             GWY_SI_UNIT_FORMAT_VFMARKUP,
                                             MAX(fabs(xmin), fabs(xmax)), 4,
                                             NULL);
    g_object_unref(siunit);

    dialog = gtk_dialog_new_with_buttons(_("Fit FD Curve"), NULL, 0, NULL);
    controls.dialog = dialog;
    gtk_dialog_add_action_widget(GTK_DIALOG(dialog),
                                 gwy_stock_like_button_new(gwy_sgettext("verb|_Fit"),
                                                           GTK_STOCK_EXECUTE),
                                 RESPONSE_FIT);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          gwy_sgettext("verb|_Estimate"), RESPONSE_ESTIMATE);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          _("_Plot Inits"), RESPONSE_PLOT);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          GTK_STOCK_SAVE, RESPONSE_SAVE);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
    gtk_dialog_add_button(GTK_DIALOG(dialog),
                          GTK_STOCK_OK, GTK_RESPONSE_OK);
    gwy_help_add_to_graph_dialog(GTK_DIALOG(dialog), GWY_HELP_DEFAULT);
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);

    hbox = gtk_hbox_new(FALSE, 2);
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 0);

    /* Graph */
    args->graph_model = gwy_graph_model_new_alike(gmodel);
    controls.graph = gwy_graph_new(args->graph_model);
    g_object_unref(args->graph_model);
    gtk_widget_set_size_request(controls.graph, 400, 300);

    gwy_graph_enable_user_input(GWY_GRAPH(controls.graph), FALSE);
    gtk_box_pack_start(GTK_BOX(hbox), controls.graph, TRUE, TRUE, 0);
    gwy_graph_set_status(GWY_GRAPH(controls.graph), GWY_GRAPH_STATUS_XSEL);

    area = GWY_GRAPH_AREA(gwy_graph_get_area(GWY_GRAPH(controls.graph)));
    selection = gwy_graph_area_get_selection(area, GWY_GRAPH_STATUS_XSEL);
    gwy_selection_set_max_objects(selection, 1);
    g_signal_connect(selection, "changed",
                     G_CALLBACK(graph_selected), &controls);

    gwy_graph_model_add_curve(controls.args->graph_model,
                              gwy_graph_model_get_curve(gmodel, args->curve));
    args->fitfunc = NULL;

    /* Controls */
    align = gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
    gtk_box_pack_start(GTK_BOX(hbox), align, FALSE, FALSE, 0);
    g_signal_connect(align, "size-request", G_CALLBACK(grow_width), NULL);

    table = gtk_table_new(7, 2, FALSE);
    gtk_table_set_row_spacings(GTK_TABLE(table), 2);
    gtk_table_set_col_spacings(GTK_TABLE(table), 6);
    gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    row = 0;

    /* Curve to fit */
    label = gtk_label_new_with_mnemonic(_("_Graph curve:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 1, row, row+1, GTK_FILL, 0, 0, 0);

    controls.curve = curve_selector_new(gmodel,
                                        G_CALLBACK(curve_changed), &controls,
                                        args->curve);
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), controls.curve);
    gtk_table_attach(GTK_TABLE(table), controls.curve,
                     1, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    row++;

    /* Fitted function */
    label = gtk_label_new_with_mnemonic(_("F_unction:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 1, row, row+1, GTK_FILL, 0, 0, 0);

    controls.function = function_selector_new(G_CALLBACK(function_changed),
                                              &controls, args->function_type);
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), controls.function);
    
    gtk_table_attach(GTK_TABLE(table), controls.function,
                     1, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);
    row++;

    controls.formula = gtk_label_new("f(x) =");
    gtk_misc_set_alignment(GTK_MISC(controls.formula), 0.0, 0.5);
    gtk_label_set_selectable(GTK_LABEL(controls.formula), TRUE);
    
    scroll = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), controls.formula);
    
    gtk_table_attach(GTK_TABLE(table), scroll,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 8);
    row++;

    /* Parameters sought */
    controls.param_table = gtk_table_new(1, 10, FALSE);
    table2 = GTK_TABLE(controls.param_table);
    gtk_table_set_row_spacing(table2, 0, 2);
    gtk_table_set_col_spacings(table2, 2);
    gtk_table_set_col_spacing(table2, 0, 6);
    gtk_table_set_col_spacing(table2, 4, 6);
    gtk_table_set_col_spacing(table2, 5, 6);
    gtk_table_set_col_spacing(table2, 7, 6);
    gtk_table_set_col_spacing(table2, 8, 6);
    gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(table2),
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    gtk_table_attach(table2, gwy_label_new_header(_("Fix")),
                     0, 1, 0, 1, GTK_FILL, 0, 0, 0);
    gtk_table_attach(table2, gwy_label_new_header(_("Parameter")),
                     1, 5, 0, 1, GTK_FILL, 0, 0, 0);
    gtk_table_attach(table2, gwy_label_new_header(_("Error")),
                     6, 8, 0, 1, GTK_FILL, 0, 0, 0);
    gtk_table_attach(table2, gwy_label_new_header(_("Initial")),
                     9, 10, 0, 1, GTK_FILL, 0, 0, 0);

    /* Make space for 4 parameters */
#if 0
    for (i = 0; i < 4; i++)
        fit_param_row_create(&controls, i, table2, i+1);
#endif

    /* Chi^2 */
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), _("χ<sup>2</sup> result:"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label,
                     0, 1, row, row+1, GTK_FILL, 0, 0, 0);

    controls.chisq = gtk_label_new(NULL);
    gtk_misc_set_alignment(GTK_MISC(controls.chisq), 0.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), controls.chisq,
                     1, 2, row, row+1, GTK_EXPAND | GTK_FILL, 0, 0, 0);

    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    /* Correlation matrix */
    expander = gtk_expander_new(NULL);
    gtk_expander_set_label_widget(GTK_EXPANDER(expander),
                                 gwy_label_new_header(_("Correlation Matrix")));
    gtk_table_attach(GTK_TABLE(table), expander,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    gtk_table_set_row_spacing(GTK_TABLE(table), row, 8);
    row++;

    align = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
    gtk_container_add(GTK_CONTAINER(expander), align);
    row++;

    controls.covar = g_array_new(FALSE, TRUE, sizeof(GtkWidget*));
    controls.covar_table = gtk_table_new(1, 1, TRUE);
    table2 = GTK_TABLE(controls.covar_table);
    gtk_table_set_col_spacings(table2, 6);
    gtk_table_set_row_spacings(table2, 2);
    gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(table2));

    /* Fit area */
    hbox2 = gtk_hbox_new(FALSE, 6);
    gtk_table_attach(GTK_TABLE(table), hbox2,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    row++;

    label = gtk_label_new(_("Range:"));
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    controls.from = gtk_entry_new();
    g_object_set_data(G_OBJECT(controls.from), "id", (gpointer)"from");
    gtk_entry_set_width_chars(GTK_ENTRY(controls.from), 8);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.from, FALSE, FALSE, 0);
    g_signal_connect(controls.from, "activate",
                     G_CALLBACK(range_changed), &controls);
    gwy_widget_set_activate_on_unfocus(controls.from, TRUE);

    label = gtk_label_new(gwy_sgettext("range|to"));
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    controls.to = gtk_entry_new();
    g_object_set_data(G_OBJECT(controls.to), "id", (gpointer)"to");
    gtk_entry_set_width_chars(GTK_ENTRY(controls.to), 8);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.to, FALSE, FALSE, 0);
    g_signal_connect(controls.to, "activate",
                     G_CALLBACK(range_changed), &controls);
    gwy_widget_set_activate_on_unfocus(controls.to, TRUE);

    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), args->abscissa_vf->units);
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    /* Auto-update */
    hbox2 = gtk_hbox_new(FALSE, 6);
    gtk_table_attach(GTK_TABLE(table), hbox2,
                     0, 2, row, row+1, GTK_FILL, 0, 0, 0);
    row++;

    label = gtk_label_new(_("Instant:"));
    gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, FALSE, 0);

    controls.auto_estimate = gtk_check_button_new_with_mnemonic(_("e_stimate"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.auto_estimate),
                                 args->auto_estimate);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.auto_estimate, FALSE, FALSE, 0);
    g_signal_connect(controls.auto_estimate, "toggled",
                     G_CALLBACK(auto_estimate_changed), &controls);

    controls.auto_plot = gtk_check_button_new_with_mnemonic(_("p_lot"));
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(controls.auto_plot),
                                 args->auto_plot);
    gtk_box_pack_start(GTK_BOX(hbox2), controls.auto_plot, FALSE, FALSE, 0);
    g_signal_connect(controls.auto_plot, "toggled",
                     G_CALLBACK(auto_plot_changed), &controls);

    function_changed(GTK_COMBO_BOX(controls.function), &controls);
    graph_selected(selection, -1, &controls);

    controls.in_update = FALSE;
    gtk_widget_show_all(dialog);

    do {
        response = gtk_dialog_run(GTK_DIALOG(dialog));
        fit_fetch_entry(&controls);
        switch (response) {
            case GTK_RESPONSE_CANCEL:
            case GTK_RESPONSE_DELETE_EVENT:
            gtk_widget_destroy(dialog);
            fit_controls_free(&controls);
            return;
            break;

            case GTK_RESPONSE_OK:
            if (args->is_estimated) {
                cmodel = gwy_graph_model_get_curve(args->graph_model, 1);
                gwy_graph_model_add_curve(gmodel, cmodel);
            }
            gtk_widget_destroy(dialog);
            break;

            case RESPONSE_SAVE:
            report = create_fit_report(args);
            gwy_save_auxiliary_data(_("Save Fit Report"), GTK_WINDOW(dialog),
                                    -1, report->str);
            g_string_free(report, TRUE);
            break;

            case RESPONSE_ESTIMATE:
            fit_estimate(&controls);
            break;

            case RESPONSE_PLOT:
            fit_set_state(&controls, FALSE, TRUE);
            fit_plot_curve(args);
            break;

            case RESPONSE_FIT:
            fit_do(&controls);
            break;

            default:
            g_assert_not_reached();
            break;
        }
    } while (response != GTK_RESPONSE_OK);
    fit_controls_free(&controls);
}
Example #15
0
static gboolean
curvature_plot_graph(GwyDataField *dfield,
                     const Intersection *i1,
                     const Intersection *i2,
                     GwyGraphModel *gmodel)
{
    GwyGraphCurveModel *gcmodel;
    GwyDataLine *dline;
    gint xres, yres;
    guint i;

    if (!gwy_graph_model_get_n_curves(gmodel)) {
        GwySIUnit *siunitxy, *siunitz;
        gchar *s;

        siunitxy = gwy_si_unit_duplicate(gwy_data_field_get_si_unit_xy(dfield));
        siunitz = gwy_si_unit_duplicate(gwy_data_field_get_si_unit_z(dfield));
        g_object_set(gmodel,
                     "title", _("Curvature Sections"),
                     "si-unit-x", siunitxy,
                     "si-unit-y", siunitz,
                     NULL);
        g_object_unref(siunitxy);
        g_object_unref(siunitz);

        for (i = 0; i < 2; i++) {
            gcmodel = gwy_graph_curve_model_new();
            s = g_strdup_printf(_("Profile %d"), (gint)i+1);
            g_object_set(gcmodel,
                         "description", s,
                         "mode", GWY_GRAPH_CURVE_LINE,
                         "color", gwy_graph_get_preset_color(i),
                         NULL);
            g_free(s);
            gwy_graph_model_add_curve(gmodel, gcmodel);
            g_object_unref(gcmodel);
        }
    }
    else {
        g_assert(gwy_graph_model_get_n_curves(gmodel) == 2);
    }

    dline = gwy_data_line_new(1, 1.0, FALSE);
    xres = gwy_data_field_get_xres(dfield);
    yres = gwy_data_field_get_yres(dfield);
    for (i = 0; i < 2; i++) {
        gint col1 = gwy_data_field_rtoj(dfield, i1[i].x);
        gint row1 = gwy_data_field_rtoi(dfield, i1[i].y);
        gint col2 = gwy_data_field_rtoj(dfield, i2[i].x);
        gint row2 = gwy_data_field_rtoi(dfield, i2[i].y);

        gwy_data_field_get_profile(dfield, dline,
                                   CLAMP(col1, 0, xres-1),
                                   CLAMP(row1, 0, yres-1),
                                   CLAMP(col2, 0, xres-1),
                                   CLAMP(row2, 0, yres-1),
                                   -1, 1, GWY_INTERPOLATION_BILINEAR);
        gwy_data_line_set_offset(dline,
                                 i1[i].t/(i2[i].t - i1[i].t)
                                 * gwy_data_line_get_real(dline));
        gcmodel = gwy_graph_model_get_curve(gmodel, i);
        gwy_graph_curve_model_set_data_from_dataline(gcmodel, dline, 0, 0);
    }
    g_object_unref(dline);

    return TRUE;
}
Example #16
0
static void
add_one_distribution(GwyContainer *container,
                     GwyDataField *dfield,
                     gint ngrains,
                     const gint *grains,
                     GwyGrainQuantity quantity,
                     gint resolution)
{
    static const GwyEnum titles[] = {
        { N_("Grain Projected Area Histogram"),
            GWY_GRAIN_VALUE_PROJECTED_AREA, },
        { N_("Grain Equivalent Square Side Histogram"),
            GWY_GRAIN_VALUE_EQUIV_SQUARE_SIDE, },
        { N_("Grain Equivalent Disc Radius Histogram"),
            GWY_GRAIN_VALUE_EQUIV_DISC_RADIUS, },
        { N_("Grain Surface Area Histogram"),
            GWY_GRAIN_VALUE_SURFACE_AREA, },
        { N_("Grain Maximum Value Histogram"),
            GWY_GRAIN_VALUE_MAXIMUM, },
        { N_("Grain Minimum Value Histogram"),
            GWY_GRAIN_VALUE_MINIMUM, },
        { N_("Grain Mean Value Histogram"),
            GWY_GRAIN_VALUE_MEAN, },
        { N_("Grain Median Value Histogram"),
            GWY_GRAIN_VALUE_MEDIAN, },
        { N_("Grain Projected Boundary Length Histogram"),
            GWY_GRAIN_VALUE_FLAT_BOUNDARY_LENGTH, },
        { N_("Grain Minimum Bounding Size Histogram"),
            GWY_GRAIN_VALUE_MINIMUM_BOUND_SIZE, },
        { N_("Grain Minimum Bounding Direction Histogram"),
            GWY_GRAIN_VALUE_MINIMUM_BOUND_ANGLE, },
        { N_("Grain Maximum Bounding Size Histogram"),
            GWY_GRAIN_VALUE_MAXIMUM_BOUND_SIZE, },
        { N_("Grain Maximum Bounding Direction Histogram"),
            GWY_GRAIN_VALUE_MAXIMUM_BOUND_ANGLE, },
        { N_("Grain Volume (Zero) Histogram"),
            GWY_GRAIN_VALUE_VOLUME_0, },
        { N_("Grain Volume (Minimum) Histogram"),
            GWY_GRAIN_VALUE_VOLUME_MIN, },
        { N_("Grain Volume (Laplacian) Histogram"),
            GWY_GRAIN_VALUE_VOLUME_LAPLACE, },
    };
    static const GwyEnum descriptions[] = {
        { N_("Grain proj. areas"),
            GWY_GRAIN_VALUE_PROJECTED_AREA, },
        { N_("Grain equiv. square sides"),
            GWY_GRAIN_VALUE_EQUIV_SQUARE_SIDE, },
        { N_("Grain equiv. disc radii"),
            GWY_GRAIN_VALUE_EQUIV_DISC_RADIUS, },
        { N_("Grain surf. areas"),
            GWY_GRAIN_VALUE_SURFACE_AREA, },
        { N_("Grain max. values"),
            GWY_GRAIN_VALUE_MAXIMUM, },
        { N_("Grain min. values"),
            GWY_GRAIN_VALUE_MINIMUM, },
        { N_("Grain mean values"),
            GWY_GRAIN_VALUE_MEAN, },
        { N_("Grain median values"),
            GWY_GRAIN_VALUE_MEDIAN, },
        { N_("Grain proj. boundary lengths"),
            GWY_GRAIN_VALUE_FLAT_BOUNDARY_LENGTH, },
        { N_("Grain min. bound. sizes"),
            GWY_GRAIN_VALUE_MINIMUM_BOUND_SIZE, },
        { N_("Grain min. bound. directions"),
            GWY_GRAIN_VALUE_MINIMUM_BOUND_ANGLE, },
        { N_("Grain max. bound. sizes"),
            GWY_GRAIN_VALUE_MAXIMUM_BOUND_SIZE, },
        { N_("Grain max. bound. directions"),
            GWY_GRAIN_VALUE_MAXIMUM_BOUND_ANGLE, },
        { N_("Grain volumes (zero)"),
            GWY_GRAIN_VALUE_VOLUME_0, },
        { N_("Grain volumes (minimum)"),
            GWY_GRAIN_VALUE_VOLUME_MIN, },
        { N_("Grain volumes (laplacian)"),
            GWY_GRAIN_VALUE_VOLUME_LAPLACE, },
    };
    GwyGraphCurveModel *cmodel;
    GwyGraphModel *gmodel;
    GwyDataLine *dataline;
    const gchar *s;

    dataline = gwy_data_field_grains_get_distribution(dfield, NULL, NULL,
                                                      ngrains, grains, quantity,
                                                      resolution);
    gmodel = gwy_graph_model_new();
    cmodel = gwy_graph_curve_model_new();
    gwy_graph_model_add_curve(gmodel, cmodel);
    g_object_unref(cmodel);

    s = gwy_enum_to_string(quantity, titles, G_N_ELEMENTS(titles));
    g_object_set(gmodel,
                 "title", _(s),
                 "axis-label-left", _("count"),
                 NULL);
    gwy_graph_model_set_units_from_data_line(gmodel, dataline);
    s = gwy_enum_to_string(quantity, descriptions, G_N_ELEMENTS(descriptions));
    g_object_set(cmodel, "description", s, NULL);
    gwy_graph_curve_model_set_data_from_dataline(cmodel, dataline, 0, 0);
    g_object_unref(dataline);

    gwy_app_data_browser_add_graph_model(gmodel, container, TRUE);
    g_object_unref(gmodel);
}