/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * smoothing/gog-exp-smooth.c : * * Copyright (C) 2006 Jean Brefort (jean.brefort@normalesup.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include #include "gog-exp-smooth.h" #include #include #include #include #include #include #include enum { EXP_SMOOTH_PROP_0, EXP_SMOOTH_PROP_STEPS, }; static GObjectClass *exp_smooth_parent_klass; #ifdef GOFFICE_WITH_GTK #include #include #include #include static void steps_changed_cb (GtkSpinButton *button, GObject *es) { g_object_set (es, "steps", gtk_spin_button_get_value_as_int (button), NULL); } static void gog_exp_smooth_populate_editor (GogObject *obj, GogEditor *editor, GogDataAllocator *dalloc, GOCmdContext *cc) { GogExpSmooth *es = GOG_EXP_SMOOTH (obj); GogDataset *set = GOG_DATASET (obj); char const *dir = go_plugin_get_dir_name ( go_plugins_get_plugin_by_id ("GOffice_smoothing")); char *path = g_build_filename (dir, "gog-exp-smooth.glade", NULL); GladeXML *gui = go_libglade_new (path, "exp-smooth-prefs", GETTEXT_PACKAGE, cc); GtkTooltips *tips = gtk_tooltips_new (); GtkWidget *label, *box, *w = glade_xml_get_widget (gui, "steps"); GtkTable *table; gtk_tooltips_set_tip (GTK_TOOLTIPS (tips), w, _("Number of interpolation steps"), NULL); gtk_spin_button_set_range (GTK_SPIN_BUTTON (w), 10, G_MAXINT); gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), es->steps); g_signal_connect (G_OBJECT (w), "value-changed", G_CALLBACK (steps_changed_cb), obj); table = GTK_TABLE (glade_xml_get_widget (gui, "exp-smooth-prefs")); w = GTK_WIDGET (gog_data_allocator_editor (dalloc, set, 0, GOG_DATA_SCALAR)); box = gtk_event_box_new (); gtk_container_add (GTK_CONTAINER (box), w); gtk_tooltips_set_tip (GTK_TOOLTIPS (tips), box, _("Default period is 10 * (xmax - xmin)/(nvalues - 1)\n" "If no value or a negative (or nul) value is provided, the " "default will be used"), NULL); gtk_widget_show_all (box); gtk_table_attach (table, box, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); label = glade_xml_get_widget (gui, "period-lbl"); g_object_set (G_OBJECT (label), "mnemonic-widget", w, NULL); g_object_set_data_full (G_OBJECT (table), "state", gui, (GDestroyNotify) g_object_unref); gog_editor_add_page (editor, table, _("Properties")); (GOG_OBJECT_CLASS (exp_smooth_parent_klass)->populate_editor) (obj, editor, dalloc, cc); } #endif static void gog_exp_smooth_update (GogObject *obj) { GogExpSmooth *es = GOG_EXP_SMOOTH (obj); GogSeries *series = GOG_SERIES (obj->parent); double const *y_vals, *x_vals; unsigned nb, i, n; double period = -1., xmin, xmax, delta, t, u, r; double *x, *y, *w, *incr; double epsilon; g_free (es->base.x); es->base.x = NULL; g_free (es->base.y); es->base.y = NULL; if (!gog_series_is_valid (series)) return; nb = gog_series_get_xy_data (series, &x_vals, &y_vals); if (nb == 0) return; x = g_new (double, nb); y = g_new (double, nb); /* Remove invalid data */ for (i = 0, n = 0; i < nb; i++) { if (!go_finite (x_vals[i]) || !go_finite (y_vals[i])) continue; x[n] = x_vals[i]; y[n++] = y_vals[i]; } go_range_min (x, n, &xmin); go_range_max (x, n, &xmax); if (n < 2) { g_free (x); g_free (y); return; } go_range_min (x, n, &xmin); go_range_max (x, n, &xmax); if (es->period->data != NULL) period = go_data_scalar_get_value ( GO_DATA_SCALAR (es->period->data)); if (period <= 0.) period = 10. * (xmax - xmin) / (n - 1); delta = (xmax - xmin) / es->steps; es->base.nb = es->steps + 1; es->base.x = g_new (double, es->base.nb); es->base.y = g_new (double, es->base.nb); incr = g_new0 (double, es->base.nb); w = g_new0 (double, es->base.nb); epsilon = DBL_EPSILON * es->steps; for (i = 0; i < n; i++) { nb = (unsigned) ceil ((x[i] - xmin) / delta - epsilon); t = pow (2., (x[i] - xmin - nb * delta) / period); incr[nb] += t * y[i]; w[nb] += t; } r = pow (2., -delta / period); t = u = 0.; for (i = 0; i < es->base.nb; i++) { t = t * r + incr[i]; u = u * r + w[i]; es->base.x[i] = xmin + i * delta; es->base.y[i] = t / u; } g_free (x); g_free (y); g_free (w); g_free (incr); gog_object_emit_changed (GOG_OBJECT (obj), FALSE); } static char const * gog_exp_smooth_type_name (G_GNUC_UNUSED GogObject const *item) { /* xgettext : the base for how to name exponentially smoothed curves objects * eg The 2nd one for a series will be called * Exponentially smoothed curve2 */ return N_("Exponentially smoothed curve"); } static void gog_exp_smooth_get_property (GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { GogExpSmooth *es = GOG_EXP_SMOOTH (obj); switch (param_id) { case EXP_SMOOTH_PROP_STEPS: g_value_set_int (value, es->steps); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec); break; } } static void gog_exp_smooth_set_property (GObject *obj, guint param_id, GValue const *value, GParamSpec *pspec) { GogExpSmooth *es = GOG_EXP_SMOOTH (obj); switch (param_id) { case EXP_SMOOTH_PROP_STEPS: es->steps = g_value_get_int (value); gog_object_request_update (GOG_OBJECT (obj)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec); return; /* NOTE : RETURN */ } } static void gog_exp_smooth_finalize (GObject *obj) { GogExpSmooth *es = GOG_EXP_SMOOTH (obj); if (es->period != NULL) { gog_dataset_finalize (GOG_DATASET (obj)); g_free (es->period); es->period = NULL; } (*exp_smooth_parent_klass->finalize) (obj); } static void gog_exp_smooth_class_init (GogSmoothedCurveClass *curve_klass) { GObjectClass *gobject_klass = (GObjectClass *) curve_klass; GogObjectClass *gog_object_klass = (GogObjectClass *) curve_klass; exp_smooth_parent_klass = g_type_class_peek_parent (curve_klass); gobject_klass->finalize = gog_exp_smooth_finalize; gobject_klass->get_property = gog_exp_smooth_get_property; gobject_klass->set_property = gog_exp_smooth_set_property; #ifdef GOFFICE_WITH_GTK gog_object_klass->populate_editor = gog_exp_smooth_populate_editor; #endif gog_object_klass->update = gog_exp_smooth_update; gog_object_klass->type_name = gog_exp_smooth_type_name; g_object_class_install_property (gobject_klass, EXP_SMOOTH_PROP_STEPS, g_param_spec_int ("steps", _("Steps"), _("Number of interpolation steps"), 10, G_MAXINT, 100, GSF_PARAM_STATIC | G_PARAM_READWRITE | GOG_PARAM_PERSISTENT)); } static void gog_exp_smooth_init (GogExpSmooth *es) { es->steps = 100; es->period = g_new0 (GogDatasetElement, 1); } static void gog_exp_smooth_dataset_dims (GogDataset const *set, int *first, int *last) { *first = 0; *last = 0; } static GogDatasetElement * gog_exp_smooth_dataset_get_elem (GogDataset const *set, int dim_i) { GogExpSmooth const *es = GOG_EXP_SMOOTH (set); g_return_val_if_fail (dim_i == 0, NULL); return es->period; } static void gog_exp_smooth_dataset_dim_changed (GogDataset *set, int dim_i) { gog_object_request_update (GOG_OBJECT (set)); } static void gog_exp_smooth_dataset_init (GogDatasetClass *iface) { iface->get_elem = gog_exp_smooth_dataset_get_elem; iface->dims = gog_exp_smooth_dataset_dims; iface->dim_changed = gog_exp_smooth_dataset_dim_changed; } GSF_DYNAMIC_CLASS_FULL (GogExpSmooth, gog_exp_smooth, NULL, NULL, gog_exp_smooth_class_init, NULL, gog_exp_smooth_init, GOG_SMOOTHED_CURVE_TYPE, 0, GSF_INTERFACE (gog_exp_smooth_dataset_init, GOG_DATASET_TYPE))