[goffice] Add control for 3D plots axes lengths. [#694746]



commit 9f09bedbb0a0234b0f7b48a4a6518fd284a9b99d
Author: Jean Brefort <jean brefort normalesup org>
Date:   Thu May 29 21:00:03 2014 +0200

    Add control for 3D plots axes lengths. [#694746]

 ChangeLog                     |   11 ++
 NEWS                          |    3 +
 goffice/Makefile.am           |    2 +
 goffice/goffice.c             |    1 +
 goffice/graph/gog-axis.c      |  378 ++++++++++++++++++++++++++++++++++++++++-
 goffice/graph/gog-axis.h      |   13 ++-
 goffice/graph/gog-chart.c     |   97 ++++++++++-
 goffice/utils/go-unit.c       |  138 +++++++++++++++
 goffice/utils/go-unit.h       |   53 ++++++
 goffice/utils/goffice-utils.h |    1 +
 10 files changed, 687 insertions(+), 10 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index e730141..d02f9d9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2014-05-29  Jean Brefort  <jean brefort normalesup org>
+
+       * goffice/Makefile.am: new GoUnit objects.
+       * goffice/goffice.c (libgoffice_shutdown): ditto.
+       * goffice/graph/gog-axis.c: Add control for 3D plots axes lengths. [#694746]
+       * goffice/graph/gog-axis.h: ditto.
+       * goffice/graph/gog-chart.c (gog_chart_view_3d_process): ditto.
+       * goffice/utils/goffice-utils.h: new GoUnit objects.
+       * goffice/utils/go-unit.c: ditto.
+       * goffice/utils/go-unit.h: ditto.
+
 2014-05-23  Morten Welinder <terra gnome org>
 
        * configure.ac: Post-release bump.
diff --git a/NEWS b/NEWS
index ade06bd..7c8cb4d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,8 @@
 goffice 0.10.17:
 
+Jean:
+       * Add control for 3D plots axes lengths. [#694746]
+
 --------------------------------------------------------------------------
 goffice 0.10.16:
 
diff --git a/goffice/Makefile.am b/goffice/Makefile.am
index 030a6a3..71e2b2d 100644
--- a/goffice/Makefile.am
+++ b/goffice/Makefile.am
@@ -395,6 +395,7 @@ utils_SOURCES =                                     \
        utils/go-rsm.c                          \
        utils/go-string.c                       \
        utils/go-undo.c                         \
+       utils/go-unit.c                         \
        utils/datetime.c                        \
        utils/formats.c                         \
        utils/go-format.c                       \
@@ -429,6 +430,7 @@ utils_HEADERS =                                     \
        utils/go-mml-to-itex.h          \
        utils/go-path.h                         \
        utils/go-pattern.h                      \
+       utils/go-unit.h                         \
        utils/go-units.h                        \
        utils/go-geometry.h                     \
        utils/go-string.h                       \
diff --git a/goffice/goffice.c b/goffice/goffice.c
index 98d49e5..9d52425 100644
--- a/goffice/goffice.c
+++ b/goffice/goffice.c
@@ -271,6 +271,7 @@ libgoffice_shutdown (void)
        _go_string_shutdown ();
        _go_locale_shutdown ();
        _go_rsm_shutdown ();
+       _go_unit_shutdown ();
 #ifdef G_OS_WIN32
        /* const_cast, we created these above */
        g_free ((char *)libgoffice_data_dir);
diff --git a/goffice/graph/gog-axis.c b/goffice/graph/gog-axis.c
index 1a1489f..2cf2c7e 100644
--- a/goffice/graph/gog-axis.c
+++ b/goffice/graph/gog-axis.c
@@ -93,6 +93,46 @@ static struct {
 
 typedef struct _GogAxisMapDesc GogAxisMapDesc;
 
+static struct {
+       GogAxisMetrics metrics;
+       const char      *name;
+} metrics_desc[GOG_AXIS_METRICS_MAX] = {
+       { GOG_AXIS_METRICS_DEFAULT, "default"},
+       { GOG_AXIS_METRICS_ABSOLUTE, "absolute"},
+       { GOG_AXIS_METRICS_RELATIVE, "relative"},
+       { GOG_AXIS_METRICS_RELATIVE_TICKS, "relative-ticks-distance"}
+};
+
+static GogAxisMetrics
+gog_axis_metrics_from_str (char const *name)
+{
+       unsigned i;
+       GogAxisMetrics ret = GOG_AXIS_METRICS_DEFAULT;
+
+       for (i = 0; i < GO_LINE_MAX; i++) {
+               if (strcmp (metrics_desc[i].name, name) == 0) {
+                       ret = metrics_desc[i].metrics;
+                       break;
+               }
+       }
+       return ret;
+}
+
+static char const *
+gog_axis_metrics_as_str (GogAxisMetrics metrics)
+{
+       unsigned i;
+       char const *ret = "default";
+
+       for (i = 0; i < GO_LINE_MAX; i++) {
+               if (metrics_desc[i].metrics == metrics) {
+                       ret = metrics_desc[i].name;
+                       break;
+               }
+       }
+       return ret;
+}
+
 struct _GogAxis {
        GogAxisBase      base;
 
@@ -126,6 +166,11 @@ struct _GogAxis {
        GogAxisColorMap const *color_map;               /* color map for color and pseudo-3d axis */
        gboolean auto_color_map;
        GogColorScale *color_scale;
+       GogAxisMetrics metrics;
+       GogAxis *ref_axis;
+       GSList *refering_axes;
+       double metrics_ratio;
+       GoUnitId unit;
 };
 
 /*****************************************************************************/
@@ -2093,7 +2138,11 @@ enum {
        AXIS_PROP_POLAR_UNIT,
        AXIS_PROP_SPAN_START,
        AXIS_PROP_SPAN_END,
-       AXIS_PROP_COLOR_MAP
+       AXIS_PROP_COLOR_MAP,
+       AXIS_PROP_METRICS,
+       AXIS_PROP_REF_AXIS,
+       AXIS_PROP_METRICS_RATIO,
+       AXIS_PROP_METRICS_UNIT
 };
 
 /*****************************************************************************/
@@ -2255,6 +2304,44 @@ gog_axis_sax_save (GOPersist const *gp, GsfXMLOut *output)
                              GO_PERSIST (axis->color_map));
 }
 
+struct axis_ref_struct {
+       GogAxis *axis;
+       char *ref;
+};
+
+static gboolean
+axis_ref_load_cb (struct axis_ref_struct *s)
+{
+       GogObject *parent = gog_object_get_parent ((GogObject *) s->axis);
+       GogAxis *axis;
+       GSList *ptr, *l;
+       l = gog_object_get_children (parent, NULL);
+       s->axis->ref_axis = NULL;
+       if (s->axis->refering_axes != NULL) {
+               s->axis->metrics = GOG_AXIS_METRICS_DEFAULT;
+               s->axis->metrics_ratio = 1.;
+               s->axis->unit = GO_UNIT_CENTIMETER;
+               goto clean;
+       }
+       for (ptr = l; (ptr != NULL) && (s->axis->ref_axis == NULL); ptr = ptr->next) {
+               if (!GOG_IS_AXIS (ptr->data))
+                       continue;
+               axis = ptr->data;
+               if (axis->metrics > GOG_AXIS_METRICS_ABSOLUTE || axis == s->axis)
+                       continue;
+               if (!strcmp (gog_object_get_name (ptr->data), s->ref)) {
+                       s->axis->ref_axis = axis;
+                       axis->refering_axes = g_slist_prepend (axis->refering_axes, s->axis);
+               }
+       }
+       g_slist_free (l);
+clean:
+       gog_object_request_update ((GogObject *) s->axis);
+       g_free (s->ref);
+       g_free (s);
+       return FALSE;
+}
+
 static void
 gog_axis_set_property (GObject *obj, guint param_id,
                       GValue const *value, GParamSpec *pspec)
@@ -2337,6 +2424,30 @@ gog_axis_set_property (GObject *obj, guint param_id,
                }
                break;
        }
+       case AXIS_PROP_METRICS:
+               axis->metrics = gog_axis_metrics_from_str (g_value_get_string (value));
+               break;
+       case AXIS_PROP_REF_AXIS: {
+               char const *str = g_value_get_string (value);
+               if ((axis->ref_axis == NULL && !strcmp (str, "none")) ||
+                   (axis->ref_axis && !strcmp (gog_object_get_name (GOG_OBJECT (axis->ref_axis)), str)))
+                       return;
+               if (strcmp (str, "none")) {
+                       struct axis_ref_struct *s = g_new (struct axis_ref_struct, 1);
+                       s->ref = g_strdup (str);
+                       s->axis = axis;
+                       g_idle_add ((GSourceFunc) axis_ref_load_cb, s);
+                       return;
+               }
+               axis->ref_axis = NULL;
+               break;
+       }
+       case AXIS_PROP_METRICS_RATIO:
+               axis->metrics_ratio = g_value_get_double (value);
+               break;
+       case AXIS_PROP_METRICS_UNIT:
+               axis->unit = (strcmp (g_value_get_string (value), "in"))? GO_UNIT_CENTIMETER: GO_UNIT_INCH;
+               break;
 
        default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
                 return; /* NOTE : RETURN */
@@ -2389,6 +2500,21 @@ gog_axis_get_property (GObject *obj, guint param_id,
        case AXIS_PROP_COLOR_MAP:
                g_value_set_string (value, (axis->auto_color_map)? "default": gog_axis_color_map_get_id 
(axis->color_map));
                break;
+       case AXIS_PROP_METRICS:
+               g_value_set_string (value, gog_axis_metrics_as_str (axis->metrics));
+               break;
+       case AXIS_PROP_REF_AXIS:
+               if (axis->ref_axis == NULL || axis->metrics < GOG_AXIS_METRICS_RELATIVE)
+                       g_value_set_string (value, "none");
+               else
+                       g_value_set_string (value, gog_object_get_name ((GogObject *) axis->ref_axis));
+               break;
+       case AXIS_PROP_METRICS_RATIO:
+               g_value_set_double (value, axis->metrics_ratio);
+               break;
+       case AXIS_PROP_METRICS_UNIT:
+               g_value_set_string (value, (axis->unit == GO_UNIT_INCH)? "in": "cm");
+               break;
 
        default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
                 break;
@@ -2830,6 +2956,101 @@ add_color_map_cb (GogAxisColorMap const *map, struct ColorMapState *state)
                gtk_combo_box_set_active_iter (state->combo, &iter);
 }
 
+struct MetricsState {
+       GogAxis *axis;
+       GtkWidget *axes, *label, *ratio, *unit;
+};
+
+static void
+metrics_ratio_changed_cb (GtkSpinButton *btn, struct MetricsState *state)
+{
+       state->axis->metrics_ratio = gtk_spin_button_get_value (btn);
+       gog_object_request_update ((GogObject *) state->axis);
+}
+
+static void
+metrics_unit_changed_cb (GtkComboBox *box, struct MetricsState *state)
+{
+       GtkTreeIter iter;
+       GtkTreeModel *model = gtk_combo_box_get_model (box);
+       gtk_combo_box_get_active_iter (box, &iter);
+       gtk_tree_model_get (model, &iter, 1, &state->axis->unit, -1);
+       gog_object_request_update ((GogObject *) state->axis);
+}
+
+static void
+metrics_axis_changed_cb (GtkComboBox *box, struct MetricsState *state)
+{
+       GogAxis *ref_axis;
+       GtkTreeIter iter;
+       GtkTreeModel *model;
+       if (state->axis->ref_axis != NULL)
+               state->axis->ref_axis->refering_axes = g_slist_remove (state->axis->ref_axis->refering_axes, 
state->axis);
+       model = gtk_combo_box_get_model (box);
+       gtk_combo_box_get_active_iter (box, &iter);
+       gtk_tree_model_get (model, &iter, 1, &ref_axis, -1);
+       ref_axis->refering_axes = g_slist_prepend (ref_axis->refering_axes, state->axis);
+       state->axis->ref_axis = ref_axis;
+       gog_object_request_update ((GogObject *) state->axis);
+}
+
+static void
+metrics_changed_cb (GtkComboBox *box, struct MetricsState *state)
+{
+       GtkTreeIter iter;
+       GtkTreeModel *model;
+       model = gtk_combo_box_get_model (box);
+       gtk_combo_box_get_active_iter (box, &iter);
+       gtk_tree_model_get (model, &iter, 1, &state->axis->metrics, -1);
+       switch (state->axis->metrics) {
+       case GOG_AXIS_METRICS_DEFAULT:
+               gtk_widget_hide (state->axes);
+               gtk_widget_hide (state->label);
+               gtk_widget_hide (state->ratio);
+               gtk_widget_hide (state->unit);
+               state->axis->ref_axis = NULL;
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->ratio), 1.);
+               gtk_combo_box_set_active (GTK_COMBO_BOX (state->unit), 0);
+               break;
+       case GOG_AXIS_METRICS_ABSOLUTE:
+               gtk_widget_hide (state->axes);
+               state->axis->ref_axis = NULL;
+               gtk_label_set_text (GTK_LABEL (state->label), _("Distance:"));
+               gtk_widget_show (state->label);
+               gtk_widget_show (state->ratio);
+               gtk_widget_show (state->unit);
+               break;
+       case GOG_AXIS_METRICS_RELATIVE:
+               gtk_widget_show (state->axes);
+               gtk_label_set_text (GTK_LABEL (state->label), _("Ratio:"));
+               gtk_widget_show (state->label);
+               gtk_widget_show (state->ratio);
+               gtk_widget_hide (state->unit);
+               if (gtk_combo_box_get_active ((GtkComboBox *) state->axes) < 0)
+                       gtk_combo_box_set_active ((GtkComboBox *) state->axes, 0);
+               else
+                       metrics_axis_changed_cb (GTK_COMBO_BOX (state->axes), state);
+               gtk_combo_box_set_active (GTK_COMBO_BOX (state->unit), 0);
+               break;
+       case GOG_AXIS_METRICS_RELATIVE_TICKS:
+               gtk_widget_show (state->axes);
+               gtk_label_set_text (GTK_LABEL (state->label), _("Ratio:"));
+               gtk_widget_show (state->label);
+               gtk_widget_show (state->ratio);
+               gtk_widget_hide (state->unit);
+               if (gtk_combo_box_get_active ((GtkComboBox *) state->axes) < 0)
+                               gtk_combo_box_set_active ((GtkComboBox *) state->axes, 0);
+               else
+                       metrics_axis_changed_cb ((GtkComboBox *) state->axes, state);
+               gtk_combo_box_set_active (GTK_COMBO_BOX (state->unit), 0);
+               break;
+       default:
+               /* will never occur */
+               break;
+       }
+       gog_object_request_update ((GogObject *) state->axis);
+}
+
 static void
 gog_axis_populate_editor (GogObject *gobj,
                          GOEditor *editor,
@@ -3014,6 +3235,117 @@ gog_axis_populate_editor (GogObject *gobj,
                                    go_gtk_builder_get_widget (gui, "color-map-grid"),
                                    _("Colors"));
        }
+       /* Metrics */
+       if ((axis->type == GOG_AXIS_X || axis->type == GOG_AXIS_Y ||
+           axis->type == GOG_AXIS_Z || axis->type == GOG_AXIS_RADIAL)
+           && (gog_chart_is_3d (GOG_CHART (gog_object_get_parent ((GogObject *) axis)))
+               && axis->refering_axes == NULL)) {
+               /* only 3d for now */
+               GogChart *parent = GOG_CHART (gog_object_get_parent ((GogObject *) axis));
+               GogAxis *axis_;
+               GtkGrid *grid;
+               GtkWidget *combo;
+               GSList *l, *ptr;
+               GtkListStore *store;
+               GtkCellRenderer *cell;
+               GtkTreeIter iter;
+               GtkAdjustment *adj;
+               GoUnit const *unit;
+               struct MetricsState *state = g_new (struct MetricsState, 1);
+               state->axis = axis;
+               w = gtk_grid_new ();
+               g_object_set (w, "border-width", 12, "column-spacing", 12, "row-spacing", 6, NULL);
+               gtk_widget_show (w);
+               grid = GTK_GRID (w);
+               store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
+               combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
+               cell = gtk_cell_renderer_text_new ();
+               gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
+               gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
+                                                               "text", 0, NULL);
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter, 0, _("Default"), 1, GOG_AXIS_METRICS_DEFAULT, -1);
+               if (axis->metrics == GOG_AXIS_METRICS_DEFAULT)
+                       gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+               if (!gog_chart_is_3d ((GogChart *) parent)) {
+                       gtk_list_store_append (store, &iter);
+                       gtk_list_store_set (store, &iter, 0, _("Absolute"), 1, GOG_AXIS_METRICS_ABSOLUTE, -1);
+                       if (axis->metrics == GOG_AXIS_METRICS_ABSOLUTE)
+                               gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+               }
+               if (axis->refering_axes == NULL) {
+                       gtk_list_store_append (store, &iter);
+                       gtk_list_store_set (store, &iter, 0, _("Relative length"), 1, 
GOG_AXIS_METRICS_RELATIVE, -1);
+                       if (axis->metrics == GOG_AXIS_METRICS_RELATIVE)
+                               gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+                       gtk_list_store_append (store, &iter);
+                       gtk_list_store_set (store, &iter, 0, _("Relative ticks distance"), 1, 
GOG_AXIS_METRICS_RELATIVE_TICKS, -1);
+                       if (axis->metrics == GOG_AXIS_METRICS_RELATIVE_TICKS)
+                               gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+               }
+               gtk_grid_attach (grid, combo, 0, 0, 3, 1);
+               gtk_widget_show ((GtkWidget *) combo);
+               state->axes = gtk_combo_box_new ();
+               store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
+               gtk_combo_box_set_model (GTK_COMBO_BOX (state->axes), GTK_TREE_MODEL (store));
+               cell = gtk_cell_renderer_text_new ();
+               gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state->axes), cell, TRUE);
+               gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (state->axes), cell,
+                                                               "text", 0, NULL);
+               l = gog_object_get_children ((GogObject *) parent, NULL);
+               for (ptr = l; ptr != NULL; ptr = ptr->next) {
+                       if (!GOG_IS_AXIS (ptr->data))
+                               continue;
+                       axis_ = ptr->data;
+                       if (axis_->metrics > GOG_AXIS_METRICS_ABSOLUTE || axis_ == axis)
+                               continue;
+                       gtk_list_store_append (store, &iter);
+                       gtk_list_store_set (store, &iter, 0, gog_object_get_name (ptr->data), 1, axis_, -1);
+                       if (axis_ == axis->ref_axis)
+                               gtk_combo_box_set_active_iter (GTK_COMBO_BOX (state->axes), &iter);
+               }
+               /* now add a spin button for the metrix_ratio */
+               gtk_grid_attach (grid, state->axes, 0, 1, 3, 1);
+               g_signal_connect (state->axes, "changed", G_CALLBACK (metrics_axis_changed_cb), state);
+               if (axis->metrics > GOG_AXIS_METRICS_ABSOLUTE)
+                       gtk_widget_show (state->axes);
+               state->label = gtk_label_new (NULL); /* the text will be set later */
+               gtk_grid_attach (grid, state->label, 0, 2, 1, 1);
+               g_signal_connect (combo, "changed", G_CALLBACK (metrics_changed_cb), state);
+               /* now add a spin button for the metrix_ratio */
+               adj = gtk_adjustment_new (1., 0.01, 100., .1, 1., 1.);
+               state->ratio = gtk_spin_button_new (adj, .1, 2);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->ratio), axis->metrics_ratio);
+               g_signal_connect (state->ratio, "value-changed", G_CALLBACK (metrics_ratio_changed_cb), 
state);
+               gtk_grid_attach (grid, state->ratio, 1, 2, 1, 1);
+               state->unit = gtk_combo_box_new ();
+               store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
+               gtk_combo_box_set_model (GTK_COMBO_BOX (state->unit), GTK_TREE_MODEL (store));
+               cell = gtk_cell_renderer_text_new ();
+               gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state->unit), cell, TRUE);
+               gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (state->unit), cell,
+                                                               "text", 0, NULL);
+               unit = go_unit_get (GO_UNIT_CENTIMETER);
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter, 0, go_unit_get_symbol (unit), 1, unit, -1);
+               if (axis->unit == GO_UNIT_CENTIMETER)
+                       gtk_combo_box_set_active_iter (GTK_COMBO_BOX (state->unit), &iter);
+               unit = go_unit_get (GO_UNIT_INCH);
+               gtk_list_store_append (store, &iter);
+               gtk_list_store_set (store, &iter, 0, go_unit_get_symbol (unit), 1, unit, -1);
+               if (axis->unit == GO_UNIT_INCH)
+                       gtk_combo_box_set_active_iter (GTK_COMBO_BOX (state->unit), &iter);
+               gtk_grid_attach (grid, state->unit, 2, 2, 1, 1);
+               g_signal_connect (state->unit, "changed", G_CALLBACK (metrics_unit_changed_cb), state);
+               if (axis->metrics >= GOG_AXIS_METRICS_ABSOLUTE) {
+                       gtk_widget_show (state->label);
+                       gtk_widget_show (state->ratio);
+                       if (axis->metrics == GOG_AXIS_METRICS_ABSOLUTE)
+                               gtk_widget_show (state->unit);
+               }
+               g_object_set_data_full ((GObject *) w, "state", state, g_free);
+               go_editor_add_page (editor, w, _("Metrics"));
+       }
 
        if (gog_object_is_visible (axis) && gog_axis_get_atype (axis) < GOG_AXIS_VIRTUAL) {
            /* Style page */
@@ -3143,6 +3475,27 @@ gog_axis_class_init (GObjectClass *gobject_klass)
                        _("The name of the color map"),
                        "default",
                        GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+       g_object_class_install_property (gobject_klass, AXIS_PROP_METRICS,
+               g_param_spec_string ("metrics", _("Metrics"),
+                       _("The way the axis ticks distance is evaluated"),
+                       "default",
+                       GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+       g_object_class_install_property (gobject_klass, AXIS_PROP_REF_AXIS,
+               g_param_spec_string ("axis-ref", _("AxisRef"),
+                       _("The name of the axis used as reference for ticks distance"),
+                       "none",
+                       GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+       g_object_class_install_property (gobject_klass, AXIS_PROP_METRICS_RATIO,
+               g_param_spec_double ("metrics-ratio",
+                       _("Metrics ratio"),
+                       _("If an axis is used as reference, gives the ratio of the ticks distance, and it the 
metrix is absolute, the ticks distance. Defaults to 1.0"),
+                   0.01, 100., 1.,
+                       GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
+       g_object_class_install_property (gobject_klass, AXIS_PROP_REF_AXIS,
+               g_param_spec_string ("metrics-unit", _("Matrics Unit"),
+                       _("The unit symbol for the absolute distance unit between ticks. Might be \"cm\" or 
\"in\""),
+                       "cm",
+                       GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
 
        gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
 
@@ -3181,6 +3534,8 @@ gog_axis_init (GogAxis *axis)
        axis->span_start = 0.;
        axis->span_end = 1.;
        axis->auto_color_map = TRUE;
+       axis->metrics_ratio = 1.;
+       axis->unit = GO_UNIT_CENTIMETER;
 }
 
 static void
@@ -3617,6 +3972,27 @@ _gog_axis_set_color_scale (GogAxis *axis, GogColorScale *scale)
        axis->color_scale = scale;
 }
 
+GogAxisMetrics
+gog_axis_get_metrics (GogAxis const *axis)
+{
+       g_return_val_if_fail (GOG_IS_AXIS (axis), GOG_AXIS_METRICS_INVALID);
+       return axis->metrics;
+}
+
+GogAxis *
+gog_axis_get_ref_axis (GogAxis const *axis)
+{
+       g_return_val_if_fail (GOG_IS_AXIS (axis) && axis->metrics > GOG_AXIS_METRICS_ABSOLUTE, NULL);
+       return axis->ref_axis;
+}
+
+double
+gog_axis_get_major_ticks_distance (GogAxis const *axis)
+{
+       g_return_val_if_fail (GOG_IS_AXIS (axis), go_nan);
+       return gog_axis_get_entry (axis, GOG_AXIS_ELEM_MAJOR_TICK, NULL);
+}
+
 /****************************************************************************/
 
 typedef GogAxisBaseView                GogAxisView;
diff --git a/goffice/graph/gog-axis.h b/goffice/graph/gog-axis.h
index 2cc1fd1..d2d107b 100644
--- a/goffice/graph/gog-axis.h
+++ b/goffice/graph/gog-axis.h
@@ -42,6 +42,15 @@ typedef enum {
        GOG_AXIS_ELEM_MAX_ENTRY
 } GogAxisElemType;
 
+typedef enum {
+       GOG_AXIS_METRICS_INVALID = -1,
+       GOG_AXIS_METRICS_DEFAULT,
+       GOG_AXIS_METRICS_ABSOLUTE,
+       GOG_AXIS_METRICS_RELATIVE,
+       GOG_AXIS_METRICS_RELATIVE_TICKS,
+       GOG_AXIS_METRICS_MAX
+} GogAxisMetrics;
+
 typedef struct {
        double           position;
        GogAxisTickTypes type;
@@ -111,7 +120,9 @@ double                      gog_axis_get_polar_perimeter    (GogAxis *axis);
 double                         gog_axis_get_circular_rotation  (GogAxis *axis);
 GogAxisColorMap const *gog_axis_get_color_map  (GogAxis *axis);
 GogColorScale *gog_axis_get_color_scale (GogAxis *axis);
-
+GogAxisMetrics gog_axis_get_metrics (GogAxis const *axis);
+GogAxis              *gog_axis_get_ref_axis (GogAxis const *axis);
+double            gog_axis_get_major_ticks_distance (GogAxis const *axis);
 /* private */
 void _gog_axis_set_color_scale (GogAxis *axis, GogColorScale *scale);
 
diff --git a/goffice/graph/gog-chart.c b/goffice/graph/gog-chart.c
index 6d69c37..1977cc4 100644
--- a/goffice/graph/gog-chart.c
+++ b/goffice/graph/gog-chart.c
@@ -1141,7 +1141,7 @@ gog_chart_view_3d_process (GogView *view, GogViewAllocation *bbox)
        /* A XYZ axis set in supposed. If new sets (cylindrical, spherical or
        other are added, we'll need to change this code */
        GogViewAllocation tmp = *bbox;
-       GogAxis *axisX, *axisY, *axisZ;
+       GogAxis *axisX, *axisY, *axisZ, *ref = NULL;
        GSList *axes;
        double xmin, xmax, ymin, ymax, zmin, zmax;
        double o[3], x[3], y[3], z[3], tg, d;
@@ -1152,6 +1152,7 @@ gog_chart_view_3d_process (GogView *view, GogViewAllocation *bbox)
        GSList *ptr;
        GogView *child;
        GogViewPadding padding;
+       GogAxisMetrics xm, ym, zm;
 
        if (!obj) {
                obj = g_object_new (GOG_3D_BOX_TYPE, NULL);
@@ -1163,25 +1164,105 @@ gog_chart_view_3d_process (GogView *view, GogViewAllocation *bbox)
        /* Only use the first of the axes. */
        axes = gog_chart_get_axes (chart, GOG_AXIS_X);
        axisX = GOG_AXIS (axes->data);
+       xm = gog_axis_get_metrics (axisX);
+       if (xm != GOG_AXIS_METRICS_DEFAULT)
+               ref = gog_axis_get_ref_axis (axisX);
        g_slist_free (axes);
        gog_axis_get_bounds (axisX, &xmin, &xmax);
        axes = gog_chart_get_axes (chart, GOG_AXIS_Y);
        axisY = GOG_AXIS (axes->data);
+       ym = gog_axis_get_metrics (axisY);
+       if (ym != GOG_AXIS_METRICS_DEFAULT && ref == NULL)
+               ref = gog_axis_get_ref_axis (axisY);
        g_slist_free (axes);
        gog_axis_get_bounds (axisY, &ymin, &ymax);
        axes = gog_chart_get_axes (chart, GOG_AXIS_Z);
        axisZ = GOG_AXIS (axes->data);
+       zm = gog_axis_get_metrics (axisZ);
+       if (zm != GOG_AXIS_METRICS_DEFAULT && ref == NULL)
+               ref = gog_axis_get_ref_axis (axisZ);
        g_slist_free (axes);
        gog_axis_get_bounds (axisZ, &zmin, &zmax);
        /* define the 3d box */
-       /* FIXME: take axes into account */
-       box_view->dz = tmp.h;
-       if (ymax - ymin > xmax - xmin) {
-               box_view->dy = tmp.w;
-               box_view->dx = (xmax - xmin) / (ymax - ymin) * tmp.w;
+       if (ref == NULL) {
+               box_view->dz = tmp.h;
+               if (ymax - ymin > xmax - xmin) {
+                       box_view->dy = tmp.w;
+                       box_view->dx = (xmax - xmin) / (ymax - ymin) * tmp.w;
+               } else {
+                       box_view->dx = tmp.w;
+                       box_view->dy = (ymax - ymin) / (xmax - xmin) * tmp.w;
+               }
        } else {
-               box_view->dx = tmp.w;
-               box_view->dy = (ymax - ymin) / (xmax - xmin) * tmp.w;
+               double ref_length, ref_tick_dist, xspan, yspan, zspan;
+               gog_axis_get_bounds (ref, &ref_length, &xspan);
+               ref_length -= xspan;
+               ref_tick_dist = gog_axis_get_major_ticks_distance (ref);
+               xspan = xmax - xmin;
+               if (xm == GOG_AXIS_METRICS_RELATIVE_TICKS) {
+                       double ratio, tick_dist = gog_axis_get_major_ticks_distance (axisX);
+                       g_object_get (axisX, "metrics-ratio", &ratio, NULL);
+                       xspan = (xmax - xmin) / tick_dist * ref_tick_dist * ratio;
+               }
+               yspan = ymax - ymin;
+               if (ym == GOG_AXIS_METRICS_RELATIVE_TICKS) {
+                       double ratio, tick_dist = gog_axis_get_major_ticks_distance (axisY);
+                       g_object_get (axisY, "metrics-ratio", &ratio, NULL);
+                       yspan = (ymax - ymin) / tick_dist * ref_tick_dist * ratio;
+               }
+               zspan = zmax - zmin;
+               if (zm == GOG_AXIS_METRICS_RELATIVE_TICKS) {
+                       double ratio, tick_dist = gog_axis_get_major_ticks_distance (axisZ);
+                       g_object_get (axisZ, "metrics-ratio", &ratio, NULL);
+                       zspan = (zmax - zmin) / tick_dist * ref_tick_dist * ratio;
+               }
+               if (ref == axisZ) {
+                       gboolean xrel = FALSE;
+                       box_view->dz = tmp.h;
+                       switch (xm) {
+                       case GOG_AXIS_METRICS_RELATIVE:
+                       case GOG_AXIS_METRICS_RELATIVE_TICKS:
+                               box_view->dx = xspan / zspan * tmp.h;
+                               if (box_view->dx > tmp.w) {
+                                       box_view->dz *= tmp.w / box_view->dx;
+                                       box_view->dx = tmp.w;
+                               }
+                                       xrel = TRUE;
+                               break;
+                       default:
+                               box_view->dx = tmp.w;
+                               break;
+                       }
+                       switch (ym) {
+                       case GOG_AXIS_METRICS_RELATIVE:
+                       case GOG_AXIS_METRICS_RELATIVE_TICKS:
+                               box_view->dy = yspan / zspan * box_view->dz;
+                               if (box_view->dy > tmp.w) {
+                                       box_view->dz *= tmp.w / box_view->dy;
+                                       if (xrel)
+                                               box_view->dx *= tmp.w / box_view->dy;
+                                       box_view->dy = tmp.w;
+                               }
+                               break;
+                       default:
+                               box_view->dy = tmp.w;
+                               break;
+                       }
+               } else {
+                       if (yspan > xspan) {
+                               box_view->dy = tmp.w;
+                               box_view->dx = xspan / yspan * tmp.w;
+                       } else {
+                               box_view->dx = tmp.w;
+                               box_view->dy = yspan / xspan * tmp.w;
+                       }
+                       if (zm == GOG_AXIS_METRICS_DEFAULT)
+                               box_view->dz = tmp.h;
+                       else
+                               box_view->dz = (ref == axisX)?
+                                                               zspan / xspan * box_view->dx:
+                                                               zspan / yspan * box_view->dy;
+               }
        }
 
        /* now compute the position of each vertex, ignoring the fov */
diff --git a/goffice/utils/go-unit.c b/goffice/utils/go-unit.c
new file mode 100644
index 0000000..51a01c4
--- /dev/null
+++ b/goffice/utils/go-unit.c
@@ -0,0 +1,138 @@
+/*
+ * go-unit.c - Units support
+ *
+ * Copyright (C) 2014 Jean Brefort (jean brefort normalesup org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 <goffice/utils/go-unit.h>
+#include <string.h>
+
+static GHashTable *units_hash = NULL;
+static GPtrArray *custom_units;
+
+struct _GoUnit {
+       char *symbol;
+       char *dim;
+       double factor_to_SI; /* how many SI units is this one: 0.0254 for inch */
+       unsigned Id;
+};
+
+static unsigned last_unit = GO_UNIT_MAX;
+
+GoUnit units[GO_UNIT_MAX] = {
+       {(char *) "m", (char *) "L", 1., GO_UNIT_METER},
+       {(char *) "cm", (char *) "L", 0.01, GO_UNIT_CENTIMETER},
+       {(char *) "in", (char *) "L", 0.0254, GO_UNIT_INCH},
+       {(char *) "pt", (char *) "L", 0.0254/72, GO_UNIT_POINT}
+};
+
+char const *
+go_unit_get_symbol (GoUnit const *unit)
+{
+       g_return_val_if_fail (unit != NULL, NULL);
+       return unit->symbol;
+}
+
+GoUnitId
+go_unit_get_id (GoUnit const *unit)
+{
+       g_return_val_if_fail (unit != NULL, GO_UNIT_UNKNOWN);
+       return unit->Id;
+}
+
+double
+go_unit_convert (GoUnit const *from, GoUnit const *to, double value)
+{
+       g_return_val_if_fail (from != NULL && to != NULL, go_nan);
+       if (strcmp (from->dim, to->dim))
+               return go_nan;
+       return value * from->factor_to_SI / to->factor_to_SI;
+}
+
+GoUnit const *go_unit_get_from_symbol (char const *symbol)
+{
+       if (units_hash == NULL)
+               _go_unit_init ();
+       return (GoUnit const *) g_hash_table_lookup (units_hash, symbol);
+}
+
+GoUnit const *
+go_unit_get (GoUnitId Id)
+{
+       if (Id < 0)
+               return NULL;
+       if (Id < GO_UNIT_MAX)
+               return units + Id;
+       else if (custom_units != NULL && Id < last_unit)
+               return g_ptr_array_index (custom_units, Id - GO_UNIT_MAX);
+       return NULL;
+}
+
+GoUnit const *
+go_unit_define (char const *symbol, char const *dim, double factor_to_SI)
+{
+       GoUnit *unit;
+       if (units_hash == NULL)
+               _go_unit_init ();
+       unit = (GoUnit *) go_unit_get_from_symbol (symbol);
+       if (unit == NULL) {
+               unit = (GoUnit *) g_new (GoUnit, 1);
+               unit->symbol = g_strdup (symbol);
+               unit->dim = g_strdup (dim);
+               unit->factor_to_SI = factor_to_SI;
+               unit->Id = last_unit++;
+               g_hash_table_replace (units_hash, unit->symbol, unit);
+               if (custom_units == NULL)
+                       custom_units = g_ptr_array_new ();
+               g_ptr_array_add (custom_units, unit);
+       }
+       return unit;
+}
+
+static void
+go_unit_destroy (GoUnit *unit)
+{
+       if (unit != NULL && unit->Id >= GO_UNIT_MAX) {
+               g_free (unit->symbol);
+               g_free (unit->dim);
+               g_free (unit);
+       }
+}
+
+void
+_go_unit_init ()
+{
+       GoUnitId Id;
+       if (units_hash != NULL)
+               return;
+       g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) go_unit_destroy);
+       for (Id = GO_UNIT_METER; Id < GO_UNIT_MAX; Id++)
+               g_hash_table_insert (units_hash, units[Id].symbol, units + Id);
+}
+
+void _go_unit_shutdown ()
+{
+       if (units_hash == NULL)
+               return;
+       g_hash_table_destroy (units_hash);
+       units_hash = NULL;
+       if (custom_units != NULL) {
+               g_ptr_array_unref (custom_units);
+               custom_units = NULL;
+       }
+}
diff --git a/goffice/utils/go-unit.h b/goffice/utils/go-unit.h
new file mode 100644
index 0000000..5253528
--- /dev/null
+++ b/goffice/utils/go-unit.h
@@ -0,0 +1,53 @@
+/*
+ * go-unit.h - Units support
+ *
+ * Copyright (C) 2014 Jean Brefort (jean brefort normalesup org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * 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
+ */
+
+#ifndef GO_UNIT_H
+#define GO_UNIT_H
+
+#include <goffice/goffice.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GoUnit GoUnit;
+
+typedef enum  {
+       GO_UNIT_UNKNOWN = -1,   
+       GO_UNIT_METER,
+       GO_UNIT_CENTIMETER,
+       GO_UNIT_INCH,
+       GO_UNIT_POINT,
+       GO_UNIT_MAX
+} GoUnitId;
+
+char const *go_unit_get_symbol (GoUnit const *unit);
+GoUnitId go_unit_get_id (GoUnit const *unit);
+double go_unit_convert (GoUnit const *from, GoUnit const *to, double value);
+GoUnit const *go_unit_get_from_symbol (char const *symbol);
+GoUnit const *go_unit_get (GoUnitId Id);
+GoUnit const *go_unit_define (char const *symbol, char const *dim, double factor_to_SI);
+
+/* private */
+void _go_unit_init (void);
+void _go_unit_shutdown (void);
+
+G_END_DECLS
+
+#endif /* GO_UNIT_H */
diff --git a/goffice/utils/goffice-utils.h b/goffice/utils/goffice-utils.h
index 6dd60f6..2e5a4e9 100644
--- a/goffice/utils/goffice-utils.h
+++ b/goffice/utils/goffice-utils.h
@@ -173,6 +173,7 @@ G_END_DECLS
 #include <goffice/utils/go-styled-object.h>
 #include <goffice/utils/go-style.h>
 #include <goffice/utils/go-undo.h>
+#include <goffice/utils/go-unit.h>
 #include <goffice/utils/go-units.h>
 #include <goffice/utils/regutf8.h>
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]