1 /*
2 * gog-xyz.c
3 *
4 * Copyright (C) 2004-2007 Jean Brefort (jean.brefort@normalesup.org)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <goffice/goffice-config.h>
23 #include "gog-xyz.h"
24 #include "gog-contour.h"
25 #include "gog-matrix.h"
26 #include "gog-surface.h"
27 #include "gog-xyz-surface.h"
28 #include "xl-surface.h"
29 #include <goffice/app/module-plugin-defs.h>
30 #include <goffice/data/go-data.h>
31 #include <goffice/data/go-data-simple.h>
32 #include <goffice/graph/gog-chart.h>
33 #include <goffice/math/go-math.h>
34 #include <goffice/utils/go-format.h>
35 #include <goffice/utils/go-persist.h>
36
37 #include <glib/gi18n-lib.h>
38 #include <gsf/gsf-impl-utils.h>
39
40 #include "embedded-stuff.c"
41
42 GOFFICE_PLUGIN_MODULE_HEADER;
43 /*-----------------------------------------------------------------------------
44 *
45 * GogContourPlot
46 *
47 *-----------------------------------------------------------------------------
48 */
49
50 enum {
51 XYZ_PROP_0,
52 XYZ_PROP_TRANSPOSED
53 };
54
55 static GogObjectClass *plot_xyz_parent_klass;
56
57 /**
58 * gog_xyz_plot_build_matrix :
59 * @plot:
60 *
61 * builds a table of normalized values: first slice = 0-1 second = 1-2,... if any.
62 **/
63
64 double *
gog_xyz_plot_build_matrix(GogXYZPlot * plot,gboolean * cardinality_changed)65 gog_xyz_plot_build_matrix (GogXYZPlot *plot, gboolean *cardinality_changed)
66 {
67 GogXYZPlotClass *klass = GOG_XYZ_PLOT_GET_CLASS (plot);
68 return klass->build_matrix (plot, cardinality_changed);
69 }
70
71 static void
gog_xyz_plot_update_3d(GogPlot * plot)72 gog_xyz_plot_update_3d (GogPlot *plot)
73 {
74 GogXYZPlot *xyz = GOG_XYZ_PLOT (plot);
75 gboolean cardinality_changed = FALSE;
76
77 if (plot->series == NULL)
78 return;
79
80 g_free (xyz->plotted_data);
81 xyz->plotted_data = gog_xyz_plot_build_matrix (xyz, &cardinality_changed);
82 if (cardinality_changed) {
83 /* gog_plot_request_cardinality_update can't be called from here
84 * since the plot might be updating.
85 */
86 GogChart *chart = GOG_CHART (GOG_OBJECT (plot)->parent);
87 plot->cardinality_valid = FALSE;
88 if (chart != NULL)
89 gog_chart_request_cardinality_update (chart);
90 }
91 }
92
93 #ifdef GOFFICE_WITH_GTK
94 extern gpointer gog_xyz_plot_pref (GogXYZPlot *plot, GOCmdContext *cc);
95 static void
gog_xyz_plot_populate_editor(GogObject * item,GOEditor * editor,G_GNUC_UNUSED GogDataAllocator * dalloc,GOCmdContext * cc)96 gog_xyz_plot_populate_editor (GogObject *item,
97 GOEditor *editor,
98 G_GNUC_UNUSED GogDataAllocator *dalloc,
99 GOCmdContext *cc)
100 {
101 if (!GOG_XYZ_PLOT (item)->data_xyz) {
102 GtkWidget *w = gog_xyz_plot_pref (GOG_XYZ_PLOT (item), cc);
103 go_editor_add_page (editor, w, _("Properties"));
104 g_object_unref (w);
105 }
106
107 (GOG_OBJECT_CLASS (plot_xyz_parent_klass)->populate_editor) (item, editor, dalloc, cc);
108 }
109 #endif
110
111 GOData *
gog_xyz_plot_get_x_vals(GogXYZPlot * plot)112 gog_xyz_plot_get_x_vals (GogXYZPlot *plot)
113 {
114 double inc;
115 double *vals;
116 unsigned i, imax;
117 if (plot->data_xyz) {
118 if (plot->x_vals == NULL) {
119 imax = plot->columns;
120 if (GOG_IS_MATRIX_PLOT (plot))
121 imax++;
122 inc = (plot->x.maxima - plot->x.minima) / (imax - 1);
123 vals = g_new (double, imax);
124 for (i = 0; i < imax; ++i)
125 vals[i] = plot->x.minima + i * inc;
126 plot->x_vals = GO_DATA (go_data_vector_val_new (vals, imax, g_free));
127 }
128 return plot->x_vals;
129 } else {
130 GogSeries *series = GOG_SERIES (plot->base.series->data);
131 return series->values[(plot->transposed)? 1: 0].data;
132 }
133 }
134
135 GOData *
gog_xyz_plot_get_y_vals(GogXYZPlot * plot)136 gog_xyz_plot_get_y_vals (GogXYZPlot *plot)
137 {
138 double inc;
139 double *vals;
140 unsigned i, imax;
141 if (plot->data_xyz) {
142 if (plot->y_vals == NULL) {
143 imax = plot->rows;
144 if (GOG_IS_MATRIX_PLOT (plot))
145 imax++;
146 inc = (plot->y.maxima - plot->y.minima) / (imax - 1);
147 vals = g_new (double, imax);
148 for (i = 0; i < imax; ++i)
149 vals[i] = plot->y.minima + i * inc;
150 plot->y_vals = GO_DATA (go_data_vector_val_new (vals, imax, g_free));
151 }
152 return plot->y_vals;
153 } else {
154 GogSeries *series = GOG_SERIES (plot->base.series->data);
155 return series->values[(plot->transposed)? 0: 1].data;
156 }
157 }
158
159 static void
gog_xyz_plot_clear_formats(GogXYZPlot * plot)160 gog_xyz_plot_clear_formats (GogXYZPlot *plot)
161 {
162 go_format_unref (plot->x.fmt);
163 plot->x.fmt = NULL;
164
165 go_format_unref (plot->y.fmt);
166 plot->y.fmt = NULL;
167
168 go_format_unref (plot->z.fmt);
169 plot->z.fmt = NULL;
170 }
171
172 static void
gog_xyz_plot_update(GogObject * obj)173 gog_xyz_plot_update (GogObject *obj)
174 {
175 GogXYZPlot * model = GOG_XYZ_PLOT(obj);
176 GogXYZSeries * series;
177 GOData *vec;
178 GOData *mat;
179 double tmp_min, tmp_max;
180
181 if (model->base.series == NULL)
182 return;
183
184 if (model->data_xyz) {
185 if (plot_xyz_parent_klass->update)
186 plot_xyz_parent_klass->update (obj);
187 return;
188 }
189
190 series = GOG_XYZ_SERIES (model->base.series->data);
191 if (!gog_series_is_valid (GOG_SERIES (series)))
192 return;
193
194 if ((vec = series->base.values[0].data) != NULL) {
195 if (model->x.fmt == NULL)
196 model->x.fmt = go_data_preferred_fmt (series->base.values[0].data);
197 model->x.date_conv = go_data_date_conv (series->base.values[0].data);
198 if (go_data_is_varying_uniformly (vec))
199 go_data_get_bounds (vec, &tmp_min, &tmp_max);
200 else
201 tmp_min = tmp_max = go_nan;
202 } else {
203 tmp_min = 0;
204 tmp_max = series->columns - 1;
205 }
206
207 if ((model->columns != series->columns)
208 || (tmp_min != model->x.minima)
209 || (tmp_max != model->x.maxima)) {
210 model->columns = series->columns;
211 model->x.minima = tmp_min;
212 model->x.maxima = tmp_max;
213 gog_axis_bound_changed (model->base.axis[(model->transposed)? GOG_AXIS_Y: GOG_AXIS_X],
214 GOG_OBJECT (model));
215 }
216
217 if ((vec = series->base.values[1].data) != NULL) {
218 if (model->y.fmt == NULL)
219 model->y.fmt = go_data_preferred_fmt (series->base.values[1].data);
220 model->y.date_conv = go_data_date_conv (series->base.values[1].data);
221 if (go_data_is_varying_uniformly (vec))
222 go_data_get_bounds (vec, &tmp_min, &tmp_max);
223 else
224 tmp_min = tmp_max = go_nan;
225 } else {
226 tmp_min = 0;
227 tmp_max = series->rows - 1;
228 }
229
230 if ((model->rows != series->rows)
231 || (tmp_min != model->y.minima)
232 || (tmp_max != model->y.maxima)) {
233 model->rows = series->rows;
234 model->y.minima = tmp_min;
235 model->y.maxima = tmp_max;
236 gog_axis_bound_changed (model->base.axis[(model->transposed)? GOG_AXIS_X: GOG_AXIS_Y],
237 GOG_OBJECT (model));
238 }
239
240 g_free (model->plotted_data);
241 model->plotted_data = NULL;
242 mat = series->base.values[2].data;
243 go_data_get_bounds (mat, &tmp_min, &tmp_max);
244 if ((tmp_min != model->z.minima)
245 || (tmp_max != model->z.maxima)) {
246 model->z.minima = tmp_min;
247 model->z.maxima = tmp_max;
248 gog_axis_bound_changed (
249 model->base.axis[GOG_XYZ_PLOT_GET_CLASS (model)->third_axis],
250 GOG_OBJECT (model));
251 } else
252 gog_xyz_plot_update_3d (GOG_PLOT (model));
253
254 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
255 if (plot_xyz_parent_klass->update)
256 plot_xyz_parent_klass->update (obj);
257 }
258
259 static GOData *
gog_xyz_plot_axis_get_bounds(GogPlot * plot,GogAxisType axis,GogPlotBoundInfo * bounds)260 gog_xyz_plot_axis_get_bounds (GogPlot *plot, GogAxisType axis,
261 GogPlotBoundInfo * bounds)
262 {
263 GogXYZSeries *series;
264 GogXYZPlot *xyz = GOG_XYZ_PLOT (plot);
265 GOData *vec = NULL;
266 double min, max;
267 GOFormat const *fmt;
268 if (!plot->series)
269 return NULL;
270 series = GOG_XYZ_SERIES (plot->series->data);
271 if ((axis == GOG_AXIS_Y && xyz->transposed) ||
272 (axis == GOG_AXIS_X && !xyz->transposed)) {
273 vec = series->base.values[0].data;
274 fmt = xyz->x.fmt;
275 min = xyz->x.minima;
276 max = xyz->x.maxima;
277 if (xyz->x.date_conv)
278 bounds->date_conv = xyz->x.date_conv;
279 } else if (axis == GOG_AXIS_X || axis == GOG_AXIS_Y) {
280 vec = series->base.values[1].data;
281 fmt = xyz->y.fmt;
282 min = xyz->y.minima;
283 max = xyz->y.maxima;
284 if (xyz->y.date_conv)
285 bounds->date_conv = xyz->y.date_conv;
286 } else {
287 if (bounds->fmt == NULL && xyz->z.fmt != NULL)
288 bounds->fmt = go_format_ref (xyz->z.fmt);
289 if (xyz->z.date_conv)
290 bounds->date_conv = xyz->z.date_conv;
291 bounds->val.minima = xyz->z.minima;
292 bounds->val.maxima = xyz->z.maxima;
293 return NULL;
294 }
295 if (bounds->fmt == NULL && fmt != NULL)
296 bounds->fmt = go_format_ref (fmt);
297 if (go_finite (min) && vec) {
298 bounds->logical.minima = bounds->val.minima = min;
299 bounds->logical.maxima = bounds->val.maxima = max;
300 bounds->is_discrete = FALSE;
301 } else {
302 bounds->val.minima = 1.;
303 bounds->logical.minima = 1.;
304 bounds->logical.maxima = go_nan;
305 bounds->is_discrete = TRUE;
306 bounds->center_on_ticks = TRUE;
307 bounds->val.maxima = ((axis == GOG_AXIS_Y && xyz->transposed) ||
308 (axis == GOG_AXIS_X && !xyz->transposed)) ?
309 series->columns:
310 series->rows;
311 if (GOG_IS_MATRIX_PLOT (plot))
312 bounds->val.maxima++;
313 }
314 return (GOData*) vec;
315 }
316
317 static void
gog_xyz_plot_finalize(GObject * obj)318 gog_xyz_plot_finalize (GObject *obj)
319 {
320 GogXYZPlot *plot = GOG_XYZ_PLOT (obj);
321 gog_xyz_plot_clear_formats (plot);
322 g_free (plot->plotted_data);
323 if (plot->x_vals != NULL)
324 g_object_unref (plot->x_vals);
325 if (plot->y_vals != NULL)
326 g_object_unref (plot->y_vals);
327 G_OBJECT_CLASS (plot_xyz_parent_klass)->finalize (obj);
328 }
329
330 static void
gog_xyz_plot_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)331 gog_xyz_plot_set_property (GObject *obj, guint param_id,
332 GValue const *value, GParamSpec *pspec)
333 {
334 GogXYZPlot *plot = GOG_XYZ_PLOT (obj);
335
336 switch (param_id) {
337 case XYZ_PROP_TRANSPOSED :
338 /* Transposed property have no meaning when data set is XYZ */
339 if (plot->data_xyz)
340 return;
341 if (!plot->transposed != !g_value_get_boolean (value)) {
342 plot->transposed = g_value_get_boolean (value);
343 if (NULL != plot->base.axis[GOG_AXIS_X])
344 gog_axis_bound_changed (plot->base.axis[GOG_AXIS_X], GOG_OBJECT (plot));
345 if (NULL != plot->base.axis[GOG_AXIS_Y])
346 gog_axis_bound_changed (plot->base.axis[GOG_AXIS_Y], GOG_OBJECT (plot));
347 g_free (plot->plotted_data);
348 plot->plotted_data = NULL;
349 }
350 break;
351
352 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
353 return; /* NOTE : RETURN */
354 }
355 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
356 }
357
358 static void
gog_xyz_plot_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)359 gog_xyz_plot_get_property (GObject *obj, guint param_id,
360 GValue *value, GParamSpec *pspec)
361 {
362 GogXYZPlot *plot = GOG_XYZ_PLOT (obj);
363
364 switch (param_id) {
365 case XYZ_PROP_TRANSPOSED :
366 g_value_set_boolean (value, plot->transposed);
367 break;
368
369 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
370 break;
371 }
372 }
373
374 static void
gog_xyz_plot_class_init(GogXYZPlotClass * klass)375 gog_xyz_plot_class_init (GogXYZPlotClass *klass)
376 {
377 GogPlotClass *gog_plot_klass = (GogPlotClass*) klass;
378 GObjectClass *gobject_klass = (GObjectClass *) klass;
379 GogObjectClass *gog_object_klass = (GogObjectClass *) klass;
380
381 plot_xyz_parent_klass = g_type_class_peek_parent (klass);
382
383 klass->get_x_vals = gog_xyz_plot_get_x_vals;
384 klass->get_y_vals = gog_xyz_plot_get_y_vals;
385
386 gobject_klass->finalize = gog_xyz_plot_finalize;
387 gobject_klass->set_property = gog_xyz_plot_set_property;
388 gobject_klass->get_property = gog_xyz_plot_get_property;
389 g_object_class_install_property (gobject_klass, XYZ_PROP_TRANSPOSED,
390 g_param_spec_boolean ("transposed",
391 _("Transposed"),
392 _("Transpose the plot"),
393 FALSE,
394 GSF_PARAM_STATIC | G_PARAM_READWRITE|GO_PARAM_PERSISTENT));
395
396 /* Fill in GOGObject superclass values */
397 gog_object_klass->update = gog_xyz_plot_update;
398 #ifdef GOFFICE_WITH_GTK
399 gog_object_klass->populate_editor = gog_xyz_plot_populate_editor;
400 #endif
401
402 {
403 static GogSeriesDimDesc dimensions[] = {
404 { N_("X"), GOG_SERIES_SUGGESTED, FALSE,
405 GOG_DIM_LABEL, GOG_MS_DIM_CATEGORIES },
406 { N_("Y"), GOG_SERIES_SUGGESTED, FALSE,
407 GOG_DIM_LABEL, GOG_MS_DIM_CATEGORIES },
408 { N_("Z"), GOG_SERIES_REQUIRED, FALSE,
409 GOG_DIM_MATRIX, GOG_MS_DIM_VALUES },
410 };
411 gog_plot_klass->desc.series.dim = dimensions;
412 gog_plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
413 gog_plot_klass->desc.series.style_fields = GO_STYLE_LINE;
414 }
415
416 /* Fill in GogPlotClass methods */
417 gog_plot_klass->desc.num_series_max = 1;
418 gog_plot_klass->series_type = gog_xyz_series_get_type();
419 gog_plot_klass->axis_get_bounds = gog_xyz_plot_axis_get_bounds;
420 gog_plot_klass->update_3d = gog_xyz_plot_update_3d;
421 }
422
423 static void
gog_xyz_plot_init(GogXYZPlot * xyz)424 gog_xyz_plot_init (GogXYZPlot *xyz)
425 {
426 xyz->rows = xyz->columns = 0;
427 xyz->transposed = FALSE;
428 xyz->data_xyz = FALSE;
429 xyz->x.minima = xyz->x.maxima = xyz->y.minima
430 = xyz->y.maxima = xyz->z.minima = xyz->z.maxima = go_nan;
431 xyz->x.fmt = xyz->y.fmt = xyz->z.fmt = NULL;
432 xyz->plotted_data = NULL;
433 xyz->x_vals = NULL;
434 xyz->y_vals = NULL;
435 }
436
GSF_DYNAMIC_CLASS_ABSTRACT(GogXYZPlot,gog_xyz_plot,gog_xyz_plot_class_init,gog_xyz_plot_init,GOG_TYPE_PLOT)437 GSF_DYNAMIC_CLASS_ABSTRACT (GogXYZPlot, gog_xyz_plot,
438 gog_xyz_plot_class_init, gog_xyz_plot_init,
439 GOG_TYPE_PLOT)
440
441 /*****************************************************************************/
442
443 static GogStyledObjectClass *series_parent_klass;
444
445 static void
446 gog_xyz_series_update (GogObject *obj)
447 {
448 GogXYZSeries *series = GOG_XYZ_SERIES (obj);
449 GODataMatrixSize size;
450 GOData *mat;
451 GOData *vec;
452 int length;
453 size.rows = 0;
454 size.columns = 0;
455
456 if (GOG_XYZ_PLOT (series->base.plot)->data_xyz) {
457 const double *x_vals, *y_vals, *z_vals = NULL;
458 series->base.num_elements = gog_series_get_xyz_data (GOG_SERIES (series),
459 &x_vals, &y_vals, &z_vals);
460 } else {
461 if (series->base.values[2].data != NULL) {
462 mat = series->base.values[2].data;
463 go_data_get_values (mat);
464 go_data_get_matrix_size (mat, &size.rows, &size.columns);
465 }
466 if (series->base.values[0].data != NULL) {
467 vec = series->base.values[0].data;
468 go_data_get_values (vec);
469 length = go_data_get_vector_size (vec);
470 if (GOG_IS_MATRIX_PLOT (series->base.plot) && length > 0)
471 length--;
472 if (length < size.columns)
473 size.columns = length;
474 }
475 if (series->base.values[1].data != NULL) {
476 vec = series->base.values[1].data;
477 go_data_get_values (vec);
478 length = go_data_get_vector_size (vec);
479 if (GOG_IS_MATRIX_PLOT (series->base.plot) && length > 0)
480 length--;
481 if (length < size.rows)
482 size.rows = length;
483 }
484 series->rows = size.rows;
485 series->columns = size.columns;
486 }
487
488 /* queue plot for redraw */
489 gog_object_request_update (GOG_OBJECT (series->base.plot));
490
491 if (series_parent_klass->base.update)
492 series_parent_klass->base.update (obj);
493 }
494
495 static void
gog_xyz_series_init_style(GogStyledObject * gso,GOStyle * style)496 gog_xyz_series_init_style (GogStyledObject *gso, GOStyle *style)
497 {
498 series_parent_klass->init_style (gso, style);
499 if (GOG_IS_MATRIX_PLOT (GOG_SERIES (gso)->plot) && style->line.auto_dash)
500 style->line.dash_type = GO_LINE_NONE;
501 }
502
503 static void
gog_xyz_series_class_init(GogStyledObjectClass * gso_klass)504 gog_xyz_series_class_init (GogStyledObjectClass *gso_klass)
505 {
506 GogObjectClass * obj_klass = (GogObjectClass *) gso_klass;
507
508 series_parent_klass = g_type_class_peek_parent (gso_klass);
509 gso_klass->init_style = gog_xyz_series_init_style;
510 obj_klass->update = gog_xyz_series_update;
511 }
512
513
GSF_DYNAMIC_CLASS(GogXYZSeries,gog_xyz_series,gog_xyz_series_class_init,NULL,GOG_TYPE_SERIES)514 GSF_DYNAMIC_CLASS (GogXYZSeries, gog_xyz_series,
515 gog_xyz_series_class_init, NULL,
516 GOG_TYPE_SERIES)
517
518 /*****************************************************************************/
519
520 G_MODULE_EXPORT void
521 go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
522 {
523 GTypeModule *module = go_plugin_get_type_module (plugin);
524 gog_xyz_plot_register_type (module);
525 gog_contour_plot_register_type (module);
526 gog_contour_view_register_type (module);
527 gog_matrix_plot_register_type (module);
528 gog_matrix_view_register_type (module);
529 gog_surface_plot_register_type (module);
530 gog_surface_view_register_type (module);
531 gog_xyz_contour_plot_register_type (module);
532 gog_xyz_matrix_plot_register_type (module);
533 gog_xyz_surface_plot_register_type (module);
534 gog_xyz_series_register_type (module);
535 gog_xy_contour_plot_register_type (module);
536 gog_xy_matrix_plot_register_type (module);
537 gog_xy_surface_plot_register_type (module);
538 xl_xyz_series_register_type (module);
539 xl_contour_plot_register_type (module);
540 xl_surface_plot_register_type (module);
541
542 register_embedded_stuff ();
543 }
544
545 G_MODULE_EXPORT void
go_plugin_shutdown(GOPlugin * plugin,GOCmdContext * cc)546 go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc)
547 {
548 unregister_embedded_stuff ();
549 }
550