Exemplo n.º 1
0
static GwyDataField*
prof_psdf(GwyDataField *dfield,
          const ProfArgs *args)
{
    GwyDataField *fftre, *fftim;
    GwySIUnit *zunit;
    const gdouble *im;
    gdouble *re;
    gint xres, yres, i;

    fftre = gwy_data_field_new_alike(dfield, FALSE);
    fftim = gwy_data_field_new_alike(dfield, FALSE);

    gwy_data_field_2dfft(dfield, NULL, fftre, fftim,
                         args->windowing,
                         GWY_TRANSFORM_DIRECTION_FORWARD,
                         GWY_INTERPOLATION_LINEAR,  /* ignored */
                         FALSE, 1);

    xres = gwy_data_field_get_xres(dfield);
    yres = gwy_data_field_get_yres(dfield);
    re = gwy_data_field_get_data(fftre);
    im = gwy_data_field_get_data_const(fftim);

    /* Put the PSDF to fftre. */
    for (i = 0; i < xres*yres; i++)
        re[i] = re[i]*re[i] + im[i]*im[i];

    g_object_unref(fftim);

    gwy_data_field_fft_postprocess(fftre, TRUE);

    zunit = gwy_data_field_get_si_unit_z(fftre);
    gwy_si_unit_power(gwy_data_field_get_si_unit_z(dfield), 2, zunit);
    gwy_si_unit_multiply(gwy_data_field_get_si_unit_xy(dfield), zunit, zunit);

    return fftre;
}
Exemplo n.º 2
0
static void
grain_stat(GwyContainer *data, GwyRunType run)
{
    GtkWidget *dialog, *table, *hbox, *button;
    GwyDataField *dfield, *mfield;
    GwySIUnit *siunit, *siunit2;
    GwySIValueFormat *vf, *vf2;
    gint xres, yres, ngrains;
    gdouble total_area, area, size, vol_0, vol_min, vol_laplace, bound_len, v;
    gdouble *values = NULL;
    gint *grains;
    GString *str, *str2;
    GPtrArray *report;
    const guchar *title;
    gchar *key, *value;
    gint row, id;
    guint i, maxlen;

    g_return_if_fail(run & STAT_RUN_MODES);
    gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD, &dfield,
                                     GWY_APP_MASK_FIELD, &mfield,
                                     GWY_APP_DATA_FIELD_ID, &id,
                                     0);
    g_return_if_fail(dfield);
    g_return_if_fail(mfield);

    report = g_ptr_array_sized_new(20);

    if (gwy_container_gis_string_by_name(data, "/filename", &title)) {
        g_ptr_array_add(report, _("File:"));
        g_ptr_array_add(report, g_strdup(title));
    }

    key = g_strdup_printf("/%d/data/title", id);
    if (gwy_container_gis_string_by_name(data, key, &title)) {
        g_ptr_array_add(report, _("Data channel:"));
        g_ptr_array_add(report, g_strdup(title));
    }
    g_free(key);

    /* Make empty line in the report */
    g_ptr_array_add(report, NULL);
    g_ptr_array_add(report, NULL);

    xres = gwy_data_field_get_xres(mfield);
    yres = gwy_data_field_get_yres(mfield);
    total_area = gwy_data_field_get_xreal(dfield)
                 *gwy_data_field_get_yreal(dfield);

    grains = g_new0(gint, xres*yres);
    ngrains = gwy_data_field_number_grains(mfield, grains);
    area = grains_get_total_value(dfield, ngrains, grains, &values,
                                  GWY_GRAIN_VALUE_PROJECTED_AREA);
    size = grains_get_total_value(dfield, ngrains, grains, &values,
                                  GWY_GRAIN_VALUE_EQUIV_SQUARE_SIDE);
    vol_0 = grains_get_total_value(dfield, ngrains, grains, &values,
                                   GWY_GRAIN_VALUE_VOLUME_0);
    vol_min = grains_get_total_value(dfield, ngrains, grains, &values,
                                     GWY_GRAIN_VALUE_VOLUME_MIN);
    vol_laplace = grains_get_total_value(dfield, ngrains, grains, &values,
                                         GWY_GRAIN_VALUE_VOLUME_LAPLACE);
    bound_len = grains_get_total_value(dfield, ngrains, grains, &values,
                                       GWY_GRAIN_VALUE_FLAT_BOUNDARY_LENGTH);
    g_free(values);
    g_free(grains);

    dialog = gtk_dialog_new_with_buttons(_("Grain Statistics"), NULL, 0,
                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
                                         NULL);
    gwy_help_add_to_proc_dialog(GTK_DIALOG(dialog), GWY_HELP_DEFAULT);

    table = gtk_table_new(10, 2, FALSE);
    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    row = 0;
    str = g_string_new(NULL);
    str2 = g_string_new(NULL);

    g_string_printf(str, "%d", ngrains);
    add_report_row(GTK_TABLE(table), &row, _("Number of grains:"),
                   str->str, str->str, report);

    siunit = gwy_data_field_get_si_unit_xy(dfield);
    siunit2 = gwy_si_unit_power(siunit, 2, NULL);

    v = area;
    vf = gwy_si_unit_get_format_with_digits(siunit2,
                                            GWY_SI_UNIT_FORMAT_VFMARKUP,
                                            v, 3, NULL);
    vf2 = gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_PLAIN,
                                             v, 3, NULL);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    g_string_printf(str2, "%.*f %s",
                    vf2->precision, v/vf2->magnitude, vf2->units);
    add_report_row(GTK_TABLE(table), &row, _("Total projected area (abs.):"),
                   str->str, str2->str, report);

    g_string_printf(str, "%.2f %%", 100.0*area/total_area);
    add_report_row(GTK_TABLE(table), &row, _("Total projected area (rel.):"),
                   str->str, str->str, report);

    v = area/ngrains;
    gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP,
                                       v, 3, vf);
    gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_PLAIN,
                                       v, 3, vf2);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    g_string_printf(str2, "%.*f %s",
                    vf2->precision, v/vf2->magnitude, vf2->units);
    add_report_row(GTK_TABLE(table), &row, _("Mean grain area:"),
                   str->str, str2->str, report);

    v = size/ngrains;
    gwy_si_unit_get_format_with_digits(siunit, GWY_SI_UNIT_FORMAT_VFMARKUP,
                                       v, 3, vf);
    gwy_si_unit_get_format_with_digits(siunit, GWY_SI_UNIT_FORMAT_PLAIN,
                                       v, 3, vf2);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    g_string_printf(str2, "%.*f %s",
                    vf2->precision, v/vf2->magnitude, vf2->units);
    add_report_row(GTK_TABLE(table), &row, _("Mean grain size:"),
                   str->str, str2->str, report);

    siunit = gwy_data_field_get_si_unit_z(dfield);
    gwy_si_unit_multiply(siunit2, siunit, siunit2);

    v = vol_0;
    gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP,
                                       v, 3, vf);
    gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_PLAIN,
                                       v, 3, vf2);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    g_string_printf(str2, "%.*f %s",
                    vf2->precision, v/vf2->magnitude, vf2->units);
    add_report_row(GTK_TABLE(table), &row, _("Total grain volume (zero):"),
                   str->str, str2->str, report);

    v = vol_min;
    gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP,
                                       v, 3, vf);
    gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_PLAIN,
                                       v, 3, vf2);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    g_string_printf(str2, "%.*f %s",
                    vf2->precision, v/vf2->magnitude, vf2->units);
    add_report_row(GTK_TABLE(table), &row, _("Total grain volume (minimum):"),
                   str->str, str2->str, report);

    v = vol_laplace;
    gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP,
                                       v, 3, vf);
    gwy_si_unit_get_format_with_digits(siunit2, GWY_SI_UNIT_FORMAT_PLAIN,
                                       v, 3, vf2);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    g_string_printf(str2, "%.*f %s",
                    vf2->precision, v/vf2->magnitude, vf2->units);
    add_report_row(GTK_TABLE(table), &row, _("Total grain volume (laplacian):"),
                   str->str, str2->str, report);

    v = bound_len;
    gwy_si_unit_get_format_with_digits(siunit, GWY_SI_UNIT_FORMAT_VFMARKUP,
                                       v, 3, vf);
    gwy_si_unit_get_format_with_digits(siunit, GWY_SI_UNIT_FORMAT_PLAIN,
                                       v, 3, vf2);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    g_string_printf(str2, "%.*f %s",
                    vf2->precision, v/vf2->magnitude, vf2->units);
    add_report_row(GTK_TABLE(table), &row, _("Total projected boundary length:"),
                   str->str, str2->str, report);

    gwy_si_unit_value_format_free(vf2);
    gwy_si_unit_value_format_free(vf);
    g_object_unref(siunit2);

    maxlen = 0;
    for (i = 0; i < report->len/2; i++) {
        key = (gchar*)g_ptr_array_index(report, 2*i);
        if (key)
            maxlen = MAX(strlen(key), maxlen);
    }

    g_string_truncate(str, 0);
    g_string_append(str, _("Grain Statistics"));
    g_string_append(str, "\n\n");

    for (i = 0; i < report->len/2; i++) {
        key = (gchar*)g_ptr_array_index(report, 2*i);
        if (key) {
            value = (gchar*)g_ptr_array_index(report, 2*i + 1);
            g_string_append_printf(str, "%-*s %s\n", maxlen+1, key, value);
            g_free(value);
        }
        else
            g_string_append_c(str, '\n');
    }
    g_ptr_array_free(report, TRUE);
    g_object_set_data(G_OBJECT(dialog), "report", str->str);

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

    button = grain_stats_add_aux_button(hbox, GTK_STOCK_SAVE,
                                        _("Save table to a file"));
    g_signal_connect_swapped(button, "clicked",
                             G_CALLBACK(grain_stat_save), dialog);

    button = grain_stats_add_aux_button(hbox, GTK_STOCK_COPY,
                                        _("Copy table to clipboard"));
    g_signal_connect_swapped(button, "clicked",
                             G_CALLBACK(grain_stat_copy), dialog);

    gtk_widget_show_all(dialog);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);

    g_string_free(str2, TRUE);
    g_string_free(str, TRUE);
}
Exemplo n.º 3
0
static GwySIUnit*
get_physical_scale(GHashTable *hash,
                   GHashTable *scannerlist,
                   GHashTable *scanlist,
                   gboolean has_version,
                   gdouble *scale,
                   GError **error)
{
    GwySIUnit *siunit, *siunit2;
    NanoscopeValue *val, *sval;
    gchar *key;
    gint q;

    /* Very old style scales (files with Version field) */
    if (!has_version) {
        if (!(val = g_hash_table_lookup(hash, "Z magnify image"))) {
            err_MISSING_FIELD(error, "Z magnify image");
            return NULL;
        }

        /* TODO: Luminescence */
        siunit = gwy_si_unit_new("m");
        /* According to Markus, the units are 1/100 nm, but his scale
         * calculation is raw/655.36 [nm].  We have the factor 1/65536 applied
         * automatically, that gives 1e-7 [m].  Whatever. */
        *scale = 1e-7 * val->hard_value;
        return siunit;
    }

    /* XXX: This is a damned heuristics.  For some value types we try to guess
     * a different quantity scale to look up. */
    if (!(val = g_hash_table_lookup(hash, "@2:Z scale"))) {
        if (!(val = g_hash_table_lookup(hash, "Z scale"))) {
            err_MISSING_FIELD(error, "Z scale");
            return NULL;
        }

        /* Old style scales */
        siunit = gwy_si_unit_new_parse(val->hard_value_units, &q);
        *scale = val->hard_value * pow10(q);
        if (val->hard_scale)
            *scale *= 65536.0/val->hard_scale;
        return siunit;
    }

    key = g_strdup_printf("@%s", val->soft_scale);

    if (!(sval = g_hash_table_lookup(scannerlist, key))
        && (!scanlist || !(sval = g_hash_table_lookup(scanlist, key)))) {
        g_warning("`%s' not found", key);
        g_free(key);
        /* XXX */
        *scale = val->hard_value;
        return gwy_si_unit_new("");
    }

    *scale = val->hard_value*sval->hard_value;

    if (!sval->hard_value_units || !*sval->hard_value_units) {
        if (gwy_strequal(val->soft_scale, "Sens. Phase"))
            siunit = gwy_si_unit_new("deg");
        else
            siunit = gwy_si_unit_new("V");
    }
    else {
        siunit = gwy_si_unit_new_parse(sval->hard_value_units, &q);
        siunit2 = gwy_si_unit_new("V");
        gwy_si_unit_multiply(siunit, siunit2, siunit);
        gwy_debug("Scale1 = %g V/LSB", val->hard_value);
        gwy_debug("Scale2 = %g %s", sval->hard_value, sval->hard_value_units);
        *scale *= pow10(q);
        gwy_debug("Total scale = %g %s/LSB",
                  *scale, gwy_si_unit_get_string(siunit,
                                                 GWY_SI_UNIT_FORMAT_PLAIN));
        g_object_unref(siunit2);
    }
    g_free(key);

    return siunit;
}
Exemplo n.º 4
0
static gboolean
stats(GwyContainer *data, GwyRunType run)
{
    GtkWidget *dialog, *table, *label;
    GwyDataField *dfield;
    GwySIUnit *siunit, *siunit2;
    GwySIValueFormat *vf;
    gint i, xres, yres, ngrains, npix;
    gdouble area, v;
    GString *str;
    gint *grains;
    gint row;

    g_return_val_if_fail(run & DIST_RUN_MODES, FALSE);
    g_return_val_if_fail(gwy_container_contains_by_name(data, "/0/mask"),
                         FALSE);

    dfield = GWY_DATA_FIELD(gwy_container_get_object_by_name(data, "/0/mask"));
    xres = gwy_data_field_get_xres(dfield);
    yres = gwy_data_field_get_yres(dfield);
    area = gwy_data_field_get_xreal(dfield)*gwy_data_field_get_yreal(dfield);
    grains = g_new0(gint, xres*yres);

    ngrains = gwy_data_field_number_grains(dfield, grains);
    npix = 0;
    for (i = 0; i < xres*yres; i++)
        npix += (grains[i] != 0);

    g_free(grains);

    dialog = gtk_dialog_new_with_buttons(_("Grain Statistics"), NULL, 0,
                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
                                         NULL);
    gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
    g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);

    table = gtk_table_new(4, 2, FALSE);
    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    row = 0;
    str = g_string_new("");

    label = gtk_label_new(_("Number of grains:"));
    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, 2, 2);
    g_string_printf(str, "%d", ngrains);
    label = gtk_label_new(str->str);
    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label, 1, 2, row, row+1,
                     GTK_FILL, 0, 2, 2);
    row++;

    /* FIXME: simplify with 2.x unit methods */
    siunit = gwy_data_field_get_si_unit_xy(dfield);
    siunit2 = GWY_SI_UNIT(gwy_si_unit_new(""));
    gwy_si_unit_multiply(siunit, siunit, siunit2);

    label = gtk_label_new(_("Total projected area (abs.):"));
    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, 2, 2);
    v = npix*area/(xres*yres);
    vf = gwy_si_unit_get_format(siunit2, v, NULL);
    g_string_printf(str, "%.*f %s",
                    vf->precision, v/vf->magnitude, vf->units);
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), str->str);
    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label, 1, 2, row, row+1,
                     GTK_FILL, 0, 2, 2);
    row++;

    label = gtk_label_new(_("Total projected area (rel.):"));
    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
    gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row+1,
                     GTK_FILL, 0, 2, 2);
    g_string_printf(str, "%.2f %%", 100.0*npix/(xres*yres));
    label = gtk_label_new(str->str);
    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label, 1, 2, row, row+1,
                     GTK_FILL, 0, 2, 2);
    row++;

    label = gtk_label_new(_("Mean grain area:"));
    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, 2, 2);
    v = npix*area/(ngrains*xres*yres);
    gwy_si_unit_get_format(siunit2, v, vf);
    g_string_printf(str, "%.*f %s",
                    vf->precision, v/vf->magnitude, vf->units);
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), str->str);
    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label, 1, 2, row, row+1,
                     GTK_FILL, 0, 2, 2);
    row++;

    label = gtk_label_new(_("Mean grain size:"));
    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, 2, 2);
    v = sqrt(npix*area/(ngrains*xres*yres));
    gwy_si_unit_get_format(siunit, v, vf);
    g_string_printf(str, "%.*f %s",
                    vf->precision, v/vf->magnitude, vf->units);
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label), str->str);
    gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
    gtk_table_attach(GTK_TABLE(table), label, 1, 2, row, row+1,
                     GTK_FILL, 0, 2, 2);
    row++;

    gwy_si_unit_value_format_free(vf);
    g_string_free(str, TRUE);
    g_object_unref(siunit2);
    gtk_widget_show_all(dialog);

    return FALSE;
}
Exemplo n.º 5
0
static void
grain_stat(G_GNUC_UNUSED GwyContainer *data, GwyRunType run)
{
    GtkWidget *dialog, *table;
    GwyDataField *dfield, *mfield;
    GwySIUnit *siunit, *siunit2;
    GwySIValueFormat *vf;
    gint xres, yres, ngrains;
    gdouble total_area, area, size, vol_0, vol_min, vol_laplace, v;
    gdouble *values = NULL;
    gint *grains;
    GString *str;
    gint row;

    g_return_if_fail(run & STAT_RUN_MODES);
    gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD, &dfield,
                                     GWY_APP_MASK_FIELD, &mfield,
                                     0);
    g_return_if_fail(dfield);
    g_return_if_fail(mfield);

    xres = gwy_data_field_get_xres(mfield);
    yres = gwy_data_field_get_yres(mfield);
    total_area = gwy_data_field_get_xreal(dfield)
                 *gwy_data_field_get_yreal(dfield);

    grains = g_new0(gint, xres*yres);
    ngrains = gwy_data_field_number_grains(mfield, grains);
    area = grains_get_total_value(dfield, ngrains, grains, &values,
                                  GWY_GRAIN_VALUE_PROJECTED_AREA);
    size = grains_get_total_value(dfield, ngrains, grains, &values,
                                  GWY_GRAIN_VALUE_EQUIV_SQUARE_SIDE);
    vol_0 = grains_get_total_value(dfield, ngrains, grains, &values,
                                   GWY_GRAIN_VALUE_VOLUME_0);
    vol_min = grains_get_total_value(dfield, ngrains, grains, &values,
                                     GWY_GRAIN_VALUE_VOLUME_MIN);
    vol_laplace = grains_get_total_value(dfield, ngrains, grains, &values,
                                         GWY_GRAIN_VALUE_VOLUME_LAPLACE);
    g_free(values);
    g_free(grains);

    dialog = gtk_dialog_new_with_buttons(_("Grain Statistics"), NULL, 0,
                                         GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
                                         NULL);
    gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);

    table = gtk_table_new(7, 2, FALSE);
    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
    row = 0;
    str = g_string_new(NULL);

    g_string_printf(str, "%d", ngrains);
    add_report_row(GTK_TABLE(table), &row, _("Number of grains:"), str->str);

    siunit = gwy_data_field_get_si_unit_xy(dfield);
    siunit2 = gwy_si_unit_power(siunit, 2, NULL);

    v = area;
    vf = gwy_si_unit_get_format(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP, v, NULL);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    add_report_row(GTK_TABLE(table), &row, _("Total projected area (abs.):"),
                   str->str);

    g_string_printf(str, "%.2f %%", 100.0*area/total_area);
    add_report_row(GTK_TABLE(table), &row, _("Total projected area (rel.):"),
                   str->str);

    v = area/ngrains;
    gwy_si_unit_get_format(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP, v, vf);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    add_report_row(GTK_TABLE(table), &row, _("Mean grain area:"), str->str);

    v = size/ngrains;
    gwy_si_unit_get_format(siunit, GWY_SI_UNIT_FORMAT_VFMARKUP, v, vf);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    add_report_row(GTK_TABLE(table), &row, _("Mean grain size:"), str->str);

    siunit = gwy_data_field_get_si_unit_z(dfield);
    gwy_si_unit_multiply(siunit2, siunit, siunit2);

    v = vol_0;
    gwy_si_unit_get_format(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP, v, vf);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    add_report_row(GTK_TABLE(table), &row, _("Total grain volume (zero):"),
                   str->str);

    v = vol_min;
    gwy_si_unit_get_format(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP, v, vf);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    add_report_row(GTK_TABLE(table), &row, _("Total grain volume (minimum):"),
                   str->str);

    v = vol_laplace;
    gwy_si_unit_get_format(siunit2, GWY_SI_UNIT_FORMAT_VFMARKUP, v, vf);
    g_string_printf(str, "%.*f %s", vf->precision, v/vf->magnitude, vf->units);
    add_report_row(GTK_TABLE(table), &row, _("Total grain volume (laplacian):"),
                   str->str);

    gwy_si_unit_value_format_free(vf);
    g_string_free(str, TRUE);
    g_object_unref(siunit2);

    gtk_widget_show_all(dialog);
    gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
}