static void laplace(GwyContainer *data, GwyRunType run) { GwyDataField *dfield, *mfield, *buffer; GQuark dquark; gdouble error, cor, maxer, lastfrac, frac, starter; gint i, id; gboolean cancelled = FALSE; g_return_if_fail(run & LAPLACE_RUN_MODES); gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD_KEY, &dquark, GWY_APP_DATA_FIELD, &dfield, GWY_APP_MASK_FIELD, &mfield, GWY_APP_DATA_FIELD_ID, &id, 0); g_return_if_fail(dfield && dquark && mfield); maxer = gwy_data_field_get_rms(dfield)/1.0e4; gwy_app_wait_start(gwy_app_find_window_for_channel(data, id), _("Laplace interpolation...")); dfield = gwy_data_field_duplicate(dfield); buffer = gwy_data_field_new_alike(dfield, TRUE); gwy_data_field_correct_average(dfield, mfield); cor = 0.2; error = 0.0; lastfrac = 0.0; starter = 0.0; for (i = 0; i < 5000; i++) { gwy_data_field_correct_laplace_iteration(dfield, mfield, buffer, cor, &error); if (error < maxer) break; if (!i) starter = error; frac = log(error/starter)/log(maxer/starter); if ((i/(gdouble)(5000)) > frac) frac = i/(gdouble)(5000); if (lastfrac > frac) frac = lastfrac; if (!gwy_app_wait_set_fraction(frac)) { cancelled = TRUE; break; } lastfrac = frac; } gwy_app_wait_finish(); if (!cancelled) { gwy_app_undo_qcheckpointv(data, 1, &dquark); gwy_container_set_object(data, dquark, dfield); gwy_app_channel_log_add_proc(data, id, id); } g_object_unref(dfield); g_object_unref(buffer); }
static void dwt(GwyContainer *data, GwyRunType run) { GtkWidget *dialog; GwyDataField *dfield; GwyDataLine *wtcoefs; DWTArgs args; gboolean ok; gint xsize, ysize, newsize, newid, oldid, i; g_return_if_fail(run & DWT_RUN_MODES); gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD, &dfield, GWY_APP_DATA_FIELD_ID, &oldid, 0); g_return_if_fail(dfield); xsize = gwy_data_field_get_xres(dfield); ysize = gwy_data_field_get_yres(dfield); if (xsize != ysize) { dialog = gtk_message_dialog_new (gwy_app_find_window_for_channel(data, oldid), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("%s: Data must be square."), "DWT"); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return; } for (newsize = 1, i = xsize-1; i; i >>= 1, newsize <<= 1) ; dwt_load_args(gwy_app_settings_get(), &args); if (run == GWY_RUN_INTERACTIVE) { ok = dwt_dialog(&args, xsize, newsize); dwt_save_args(gwy_app_settings_get(), &args); if (!ok) return; } dfield = gwy_data_field_new_resampled(dfield, newsize, newsize, args.interp); gwy_data_field_add(dfield, -gwy_data_field_get_avg(dfield)); wtcoefs = GWY_DATA_LINE(gwy_data_line_new(10, 10, TRUE)); wtcoefs = gwy_dwt_set_coefficients(wtcoefs, args.wavelet); gwy_data_field_dwt(dfield, wtcoefs, 1, 4); newid = gwy_app_data_browser_add_data_field(dfield, data, TRUE); g_object_unref(dfield); gwy_app_set_data_field_title(data, newid, _("DWT")); gwy_app_sync_data_items(data, data, oldid, newid, FALSE, GWY_DATA_ITEM_PALETTE, 0); g_object_unref(wtcoefs); }
static void grain_filter(GwyContainer *data, GwyRunType run) { GwySIUnit *siunitxy, *siunitz; GFilterArgs args; GwyDataField *dfield, *mfield; GQuark mquark; gint id; g_return_if_fail(run & GFILTER_RUN_MODES); gfilter_load_args(gwy_app_settings_get(), &args); gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD, &dfield, GWY_APP_MASK_FIELD, &mfield, GWY_APP_MASK_FIELD_KEY, &mquark, GWY_APP_DATA_FIELD_ID, &id, 0); g_return_if_fail(dfield && mfield); siunitxy = gwy_data_field_get_si_unit_xy(dfield); siunitz = gwy_data_field_get_si_unit_z(dfield); args.units_equal = gwy_si_unit_equal(siunitxy, siunitz); args.valuedata = calculate_all_grain_values(dfield, mfield, &args.ngrains, &args.grains); args.sortedvaluedata = sort_grain_values(args.valuedata, &args.nuniqvalues, args.ngrains); if (run == GWY_RUN_INTERACTIVE && !args.ngrains) { GtkWidget *dialog; gfilter_free_args(&args); dialog = gtk_message_dialog_new (gwy_app_find_window_for_channel(data, id), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("There are no grains to filter.")); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return; } if (run == GWY_RUN_IMMEDIATE) { run_noninteractive(&args, data, mfield, mquark); gwy_app_channel_log_add_proc(data, id, id); } else gfilter_dialog(&args, data, dfield, mfield, id, mquark); gfilter_free_args(&args); }
static void require_same_units_dialog(GwyContainer *data, gint id) { GtkWidget *dialog; dialog = gtk_message_dialog_new(gwy_app_find_window_for_channel(data, id), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Grain correlation: Lateral dimensions " "and value must be the same physical " "quantity for the selected grain " "properties.")); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return; }
static void curvature(GwyContainer *data, GwyRunType run) { GwyDataField *dfield, *mfield; CurvatureArgs args; gboolean ok; gint id; g_return_if_fail(run & CURVATURE_RUN_MODES); g_return_if_fail(g_type_from_name("GwyLayerLine")); 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); if (!gwy_si_unit_equal(gwy_data_field_get_si_unit_xy(dfield), gwy_data_field_get_si_unit_z(dfield))) { GtkWidget *dialog; dialog = gtk_message_dialog_new (gwy_app_find_window_for_channel(data, id), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("%s: Lateral dimensions and value must " "be the same physical quantity."), _("Curvature")); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return; } load_args(gwy_app_settings_get(), &args); if (run == GWY_RUN_INTERACTIVE) { ok = curvature_dialog(&args, data, dfield, mfield, id); save_args(gwy_app_settings_get(), &args); if (!ok) return; } curvature_do(data, dfield, mfield, id, &args); if (args.set_selection) gwy_app_channel_log_add_proc(data, id, id); }
/* compute corelation */ static gboolean get_score_iteratively(GwyDataField *data_field, GwyDataField *kernel_field, GwyDataField *score, MergeArgs *args) { enum { WORK_PER_UPDATE = 50000000 }; GwyComputationState *state; GwyContainer *data; gboolean ok = FALSE; int work, wpi; work = 0; wpi = gwy_data_field_get_xres(kernel_field) *gwy_data_field_get_yres(kernel_field); wpi = MIN(wpi, WORK_PER_UPDATE); state = gwy_data_field_correlate_init(data_field, kernel_field, score); /* FIXME */ data = gwy_app_data_browser_get(args->op1.datano); gwy_app_wait_start(gwy_app_find_window_for_channel(data, args->op1.id), _("Initializing...")); gwy_data_field_correlate_iteration(state); if (!gwy_app_wait_set_message(_("Correlating..."))) goto get_score_fail; do { gwy_data_field_correlate_iteration(state); work += wpi; if (work > WORK_PER_UPDATE) { work -= WORK_PER_UPDATE; if (!gwy_app_wait_set_fraction(state->fraction)) goto get_score_fail; } } while (state->state != GWY_COMPUTATION_STATE_FINISHED); ok = TRUE; get_score_fail: gwy_data_field_correlate_finalize(state); gwy_app_wait_finish(); return ok; }
static gboolean xydenoise_do(XYdenoiseArgs *args) { GwyContainer *data; GwyDataField *dfieldx, *rx, *ix, *dfieldy, *ry, *iy, *result, *iresult; gint i, newid, xres, yres; gdouble *rxdata, *rydata, *ixdata, *iydata; GwyWindowingType window = GWY_WINDOWING_NONE; GwyInterpolationType interp = GWY_INTERPOLATION_LINEAR; GQuark quark; data = gwy_app_data_browser_get(args->op1.datano); gwy_app_wait_start(gwy_app_find_window_for_channel(data, args->op1.id), _("Initializing...")); quark = gwy_app_get_data_key_for_id(args->op1.id); dfieldx = GWY_DATA_FIELD(gwy_container_get_object(data, quark)); data = gwy_app_data_browser_get(args->op2.datano); quark = gwy_app_get_data_key_for_id(args->op2.id); dfieldy = GWY_DATA_FIELD(gwy_container_get_object(data, quark)); xres = gwy_data_field_get_xres(dfieldx); yres = gwy_data_field_get_yres(dfieldy); result = gwy_data_field_new_alike(dfieldx, TRUE); iresult = gwy_data_field_new_alike(dfieldx, TRUE); rx = gwy_data_field_new_alike(dfieldx, TRUE); ix = gwy_data_field_new_alike(dfieldx, TRUE); ry = gwy_data_field_new_alike(dfieldx, TRUE); iy = gwy_data_field_new_alike(dfieldx, TRUE); gwy_app_wait_set_fraction(0.1); gwy_app_wait_set_message(_("Computing forward FFTs...")); gwy_data_field_2dfft(dfieldx, NULL, rx, ix, window, GWY_TRANSFORM_DIRECTION_FORWARD, interp, FALSE, 0); gwy_data_field_2dfft(dfieldy, NULL, ry, iy, window, GWY_TRANSFORM_DIRECTION_FORWARD, interp, FALSE, 0); rxdata = gwy_data_field_get_data(rx); rydata = gwy_data_field_get_data(ry); ixdata = gwy_data_field_get_data(ix); iydata = gwy_data_field_get_data(iy); gwy_app_wait_set_fraction(0.3); gwy_app_wait_set_message(_("Computing image...")); for (i = 0; i < xres*yres; i++) { gdouble xmodule = sqrt(rxdata[i]*rxdata[i] + ixdata[i]*ixdata[i]); gdouble xphase = atan2(ixdata[i],rxdata[i]); gdouble ymodule = sqrt(rydata[i]*rydata[i] + iydata[i]*iydata[i]); /*gdouble yphase = atan2(iydata[i],rydata[i]);*/ rxdata[i] = MIN(xmodule, ymodule)*cos(xphase); ixdata[i] = MIN(xmodule, ymodule)*sin(xphase); } gwy_app_wait_set_fraction(0.7); gwy_app_wait_set_message(_("Computing backward FFT...")); gwy_data_field_2dfft(rx, ix, result, iresult, window, GWY_TRANSFORM_DIRECTION_BACKWARD, interp, FALSE, 0); gwy_app_wait_set_fraction(0.9); data = gwy_app_data_browser_get(args->op1.datano); newid = gwy_app_data_browser_add_data_field(result, data, TRUE); gwy_app_sync_data_items(data, data, args->op1.id, newid, FALSE, GWY_DATA_ITEM_GRADIENT, 0); gwy_app_set_data_field_title(data, newid, _("Denoised")); gwy_app_channel_log_add_proc(data, -1, newid); gwy_app_wait_finish(); g_object_unref(result); g_object_unref(iresult); g_object_unref(rx); g_object_unref(ix); g_object_unref(ry); g_object_unref(iy); return TRUE; }
static void cwt(GwyContainer *data, GwyRunType run) { GtkWidget *dialog; GwyDataField *dfield; CWTArgs args; gboolean ok; gint xsize, ysize; gint newsize; gint oldid, newid; g_return_if_fail(run & CWT_RUN_MODES); gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD, &dfield, GWY_APP_DATA_FIELD_ID, &oldid, 0); g_return_if_fail(dfield); xsize = gwy_data_field_get_xres(dfield); ysize = gwy_data_field_get_yres(dfield); if (xsize != ysize) { dialog = gtk_message_dialog_new (gwy_app_find_window_for_channel(data, oldid), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("%s: Data must be square."), "CWT"); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return; } cwt_load_args(gwy_app_settings_get(), &args); if (run == GWY_RUN_INTERACTIVE) { ok = cwt_dialog(&args); cwt_save_args(gwy_app_settings_get(), &args); if (!ok) return; } dfield = gwy_data_field_duplicate(dfield); newsize = gwy_fft_find_nice_size(xsize); gwy_data_field_resample(dfield, newsize, newsize, args.interp); gwy_data_field_cwt(dfield, args.interp, args.scale, args.wavelet); if (args.preserve) gwy_data_field_resample(dfield, xsize, ysize, args.interp); newid = gwy_app_data_browser_add_data_field(dfield, data, TRUE); gwy_app_sync_data_items(data, data, oldid, newid, FALSE, GWY_DATA_ITEM_GRADIENT, GWY_DATA_ITEM_MASK_COLOR, 0); g_object_unref(dfield); gwy_app_set_data_field_title(data, newid, _("CWT")); }
static void dwt_anisotropy(GwyContainer *data, GwyRunType run) { GtkWidget *dialog; GwyDataField *dfield, *mask; GQuark dquark, mquark; GwyDataLine *wtcoefs; DWTAnisotropyArgs args; gboolean ok; gint xsize, ysize, newsize, limit, id, i; g_return_if_fail(run & DWT_ANISOTROPY_RUN_MODES); gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD_KEY, &dquark, GWY_APP_DATA_FIELD, &dfield, GWY_APP_DATA_FIELD_ID, &id, GWY_APP_MASK_FIELD_KEY, &mquark, GWY_APP_MASK_FIELD, &mask, 0); g_return_if_fail(dfield && dquark); xsize = gwy_data_field_get_xres(dfield); ysize = gwy_data_field_get_yres(dfield); if (xsize != ysize) { dialog = gtk_message_dialog_new (gwy_app_find_window_for_channel(data, id), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("%s: Data must be square."), _("DWT Anisotropy")); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return; } dwt_anisotropy_load_args(gwy_app_settings_get(), &args); if (run == GWY_RUN_INTERACTIVE) { ok = dwt_anisotropy_dialog(&args); dwt_anisotropy_save_args(gwy_app_settings_get(), &args); if (!ok) return; } for (newsize = 1, i = xsize-1; i; i >>= 1, newsize <<= 1) ; dfield = gwy_data_field_new_resampled(dfield, newsize, newsize, args.interp); gwy_data_field_add(dfield, -gwy_data_field_get_avg(dfield)); gwy_app_undo_qcheckpoint(data, dquark, mquark, 0); if (!mask) { mask = gwy_data_field_new_alike(dfield, FALSE); gwy_container_set_object(data, mquark, mask); g_object_unref(mask); } gwy_data_field_resample(mask, newsize, newsize, GWY_INTERPOLATION_NONE); wtcoefs = gwy_data_line_new(10, 10, TRUE); wtcoefs = gwy_dwt_set_coefficients(wtcoefs, args.wavelet); /*justo for sure clamp the lowlimit again*/ limit = pow(2, CLAMP(args.lowlimit, 1, 20)); gwy_data_field_dwt_mark_anisotropy(dfield, mask, wtcoefs, args.ratio, limit); gwy_data_field_resample(mask, xsize, ysize, GWY_INTERPOLATION_ROUND); g_object_unref(wtcoefs); g_object_unref(dfield); gwy_data_field_data_changed(mask); gwy_app_channel_log_add_proc(data, id, id); }
static void median(GwyContainer *data, GwyRunType run) { GwyDataField *dfield, *background = NULL; MedianBgArgs args; gint oldid, newid; GQuark dquark; gdouble xr, yr; gboolean ok = TRUE; g_return_if_fail(run & MEDIANBG_RUN_MODES); gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD, &dfield, GWY_APP_DATA_FIELD_KEY, &dquark, GWY_APP_DATA_FIELD_ID, &oldid, 0); g_return_if_fail(dfield && dquark); median_load_args(gwy_app_settings_get(), &args); /* FIXME: this is bogus for non-square pixels anyway */ xr = gwy_data_field_get_xreal(dfield)/gwy_data_field_get_xres(dfield); yr = gwy_data_field_get_yreal(dfield)/gwy_data_field_get_yres(dfield); args.pixelsize = hypot(xr, yr); args.valform = gwy_data_field_get_value_format_xy(dfield, GWY_SI_UNIT_FORMAT_VFMARKUP, NULL); gwy_debug("pixelsize = %g, vf = (%g, %d, %s)", args.pixelsize, args.valform->magnitude, args.valform->precision, args.valform->units); if (run == GWY_RUN_INTERACTIVE) { ok = median_dialog(&args); median_save_args(gwy_app_settings_get(), &args); } gwy_si_unit_value_format_free(args.valform); if (!ok) return; gwy_app_wait_start(gwy_app_find_window_for_channel(data, oldid), _("Median-leveling...")); background = median_background(GWY_ROUND(args.size), dfield); gwy_app_wait_finish(); if (!background) return; gwy_app_undo_qcheckpointv(data, 1, &dquark); gwy_data_field_subtract_fields(dfield, dfield, background); gwy_data_field_data_changed(dfield); gwy_app_channel_log_add_proc(data, oldid, oldid); if (!args.do_extract) { g_object_unref(background); return; } newid = gwy_app_data_browser_add_data_field(background, data, TRUE); g_object_unref(background); gwy_app_sync_data_items(data, data, oldid, newid, FALSE, GWY_DATA_ITEM_GRADIENT, 0); gwy_app_set_data_field_title(data, newid, _("Background")); gwy_app_channel_log_add(data, oldid, newid, NULL, NULL); }
static void level_grains_do(const LevelGrainsArgs *args, GwyContainer *data, GQuark dquark, gint id, GwyDataField *dfield, GwyDataField *mfield) { GwyDataField *buffer, *background, *invmask; gdouble error, cor, maxerr, lastfrac, frac, starterr; gdouble *heights, *bgdata; gint *grains; gboolean cancelled = FALSE; gint i, xres, yres, ngrains; xres = gwy_data_field_get_xres(mfield); yres = gwy_data_field_get_yres(mfield); grains = g_new0(gint, xres*yres); ngrains = gwy_data_field_number_grains(mfield, grains); if (!ngrains) { g_free(grains); return; } heights = g_new(gdouble, ngrains+1); gwy_data_field_grains_get_values(dfield, heights, ngrains, grains, args->base); heights[0] = 0.0; background = gwy_data_field_new_alike(dfield, FALSE); bgdata = gwy_data_field_get_data(background); for (i = 0; i < xres*yres; i++) bgdata[i] = -heights[grains[i]]; invmask = gwy_data_field_duplicate(mfield); gwy_data_field_multiply(invmask, -1.0); gwy_data_field_add(invmask, 1.0); maxerr = gwy_data_field_get_rms(dfield)/1.0e4; gwy_app_wait_start(gwy_app_find_window_for_channel(data, id), _("Laplace interpolation...")); g_free(heights); g_free(grains); buffer = gwy_data_field_new_alike(background, TRUE); gwy_data_field_correct_average(background, invmask); cor = 0.2; error = 0.0; lastfrac = 0.0; starterr = 0.0; for (i = 0; i < 5000; i++) { gwy_data_field_correct_laplace_iteration(background, invmask, buffer, cor, &error); if (error < maxerr) break; if (!i) starterr = error; frac = log(error/starterr)/log(maxerr/starterr); if ((i/(gdouble)(5000)) > frac) frac = i/(gdouble)(5000); if (lastfrac > frac) frac = lastfrac; if (!gwy_app_wait_set_fraction(frac)) { cancelled = TRUE; break; } lastfrac = frac; } gwy_app_wait_finish(); if (!cancelled) { gwy_data_field_invert(background, FALSE, FALSE, TRUE); gwy_app_undo_qcheckpointv(data, 1, &dquark); gwy_data_field_subtract_fields(dfield, dfield, background); gwy_data_field_data_changed(dfield); if (args->do_extract) { gint newid; newid = gwy_app_data_browser_add_data_field(background, data, TRUE); gwy_app_sync_data_items(data, data, id, newid, FALSE, GWY_DATA_ITEM_GRADIENT, 0); gwy_app_set_data_field_title(data, newid, _("Background")); } } g_object_unref(buffer); g_object_unref(invmask); g_object_unref(background); }
static void maskcor_do(MaskcorArgs *args) { enum { WORK_PER_UPDATE = 50000000 }; GwyDataField *dfield, *kernel, *retfield, *score; GwyContainer *data, *kerneldata; GwyComputationState *state; GQuark quark; gint newid, work, wpi; kerneldata = gwy_app_data_browser_get(args->kernel.datano); quark = gwy_app_get_data_key_for_id(args->kernel.id); kernel = GWY_DATA_FIELD(gwy_container_get_object(kerneldata, quark)); data = gwy_app_data_browser_get(args->data.datano); quark = gwy_app_get_data_key_for_id(args->data.id); dfield = GWY_DATA_FIELD(gwy_container_get_object(data, quark)); retfield = gwy_data_field_new_alike(dfield, FALSE); /* FIXME */ if (args->method == GWY_CORRELATION_NORMAL) { gwy_app_wait_start(gwy_app_find_window_for_channel(data, args->data.id), _("Initializing...")); state = gwy_data_field_correlate_init(dfield, kernel, retfield); gwy_app_wait_set_message(_("Correlating...")); work = 0; wpi = gwy_data_field_get_xres(kernel)*gwy_data_field_get_yres(kernel); wpi = MIN(wpi, WORK_PER_UPDATE); do { gwy_data_field_correlate_iteration(state); work += wpi; if (work > WORK_PER_UPDATE) { work -= WORK_PER_UPDATE; if (!gwy_app_wait_set_fraction(state->fraction)) { gwy_data_field_correlate_finalize(state); gwy_app_wait_finish(); g_object_unref(retfield); return; } } } while (state->state != GWY_COMPUTATION_STATE_FINISHED); gwy_data_field_correlate_finalize(state); gwy_app_wait_finish(); } else gwy_data_field_correlate(dfield, kernel, retfield, args->method); /* score - do new data with score */ if (args->result == GWY_MASKCOR_SCORE) { score = gwy_data_field_duplicate(retfield); newid = gwy_app_data_browser_add_data_field(score, data, TRUE); gwy_app_sync_data_items(data, data, args->data.id, newid, FALSE, GWY_DATA_ITEM_GRADIENT, 0); gwy_app_set_data_field_title(data, newid, _("Correlation score")); g_object_unref(score); gwy_app_channel_log_add_proc(data, args->data.id, newid); } else { /* add mask */ quark = gwy_app_get_mask_key_for_id(args->data.id); gwy_app_undo_qcheckpointv(data, 1, &quark); if (args->result == GWY_MASKCOR_OBJECTS) plot_correlated(retfield, gwy_data_field_get_xres(kernel), gwy_data_field_get_yres(kernel), args->threshold); else if (args->result == GWY_MASKCOR_MAXIMA) gwy_data_field_threshold(retfield, args->threshold, 0.0, 1.0); gwy_container_set_object(data, quark, retfield); gwy_app_channel_log_add_proc(data, args->data.id, args->data.id); } g_object_unref(retfield); }