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