[goffice] Fixed area plots issues.



commit 06c6cac61f5b4332d94deda3847c6c1d8285f853
Author: Jean Brefort <jean brefort normalesup org>
Date:   Mon Aug 5 13:51:20 2013 +0200

    Fixed area plots issues.

 ChangeLog                                |   18 ++
 NEWS                                     |    3 +-
 docs/reference/goffice-0.10-sections.txt |   61 ++++++-
 goffice/graph/gog-chart-map.c            |    5 +-
 goffice/math/go-matrix.c                 |    1 +
 goffice/utils/go-path.c                  |  187 +++++++++++++++++-
 goffice/utils/go-path.h                  |    9 +
 plugins/plot_barcol/gog-line.c           |  331 +++++++++++++++++++++++++++---
 plugins/plot_xy/gog-xy.c                 |    4 +-
 9 files changed, 576 insertions(+), 43 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index eb10100..702bcc8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2013-08-05  Jean Brefort  <jean brefort normalesup org>
+
+       * docs/reference/goffice-0.10-sections.txt: make gtk-doc happy.
+       * goffice/graph/gog-chart-map.c (xy_make_path_step): remove an unneeded
+       lineto instruction.
+       * goffice/math/go-matrix.c: make gtk-doc happy.
+       * goffice/utils/go-path.c (go_path_add_points),
+       (go_path_interpret_full), (go_path_copy),
+       (go_path_copy_restricted), (go_path_append): two new functions and sanitize
+       a few others.
+       * goffice/utils/go-path.h: new functions.
+       * plugins/plot_barcol/gog-line.c (gog_line_view_get_data_at_point),
+       (gog_line_view_render):, (gog_line_view_class_init): implement 
+       get_data_at_point for area and line plots and fix rendring of stacked area
+       plots. [#627277][#689661][#705034]
+       * plugins/plot_xy/gog-xy.c (gog_xy_view_get_data_at_point): minor
+       optimization.
+
 2013-08-02  Jean Brefort  <jean brefort normalesup org>
 
        * plugins/plot_xy/gog-bubble-prefs.ui: fix area vs diameter selection.
diff --git a/NEWS b/NEWS
index d933fcc..4834355 100644
--- a/NEWS
+++ b/NEWS
@@ -8,7 +8,8 @@ Jean:
        * Fixed text and path items positions. [see #704391]
        * Don't crash when a pixbuf has an invalid size. [#704560][#705005]
        * Fix area vs diameter selection in bubble plots. [#705312]
-
+       * Implement get_data_at_point for area and line plots. [#627277][#689661]
+       * Fix rendring of stacked area plots. [#705034]
 
 Morten:
        * Add prescaling to go_linear_regression_leverage.  [#703381]
diff --git a/docs/reference/goffice-0.10-sections.txt b/docs/reference/goffice-0.10-sections.txt
index d799f7a..7dc78d2 100644
--- a/docs/reference/goffice-0.10-sections.txt
+++ b/docs/reference/goffice-0.10-sections.txt
@@ -1622,10 +1622,12 @@ go_path_arc_to
 go_path_clear
 go_path_close
 go_path_copy
+go_path_copy_restricted
 go_path_curve_to
 go_path_free
 go_path_get_options
 go_path_interpret
+go_path_interpret_full
 go_path_line_to
 go_path_move_to
 go_path_new
@@ -1857,8 +1859,6 @@ go_plugin_service_simple_get_type
 <SECTION>
 <FILE>go-quad</FILE>
 <TITLE>GOQuad</TITLE>
-GOQuad
-GOQuadl
 go_quad_add
 go_quad_addl
 go_quad_div
@@ -1883,6 +1883,63 @@ go_quad_sub
 go_quad_subl
 go_quad_value
 go_quad_valuel
+<SUBSECTION Private>
+GOQuad
+GOQuadl
+</SECTION>
+
+<SECTION>
+<FILE>go-quad-matrix</FILE>
+<TITLE>GOQuadMatrix</TITLE>
+go_quad_matrix_back_solve
+go_quad_matrix_back_solvel
+go_quad_matrix_copy
+go_quad_matrix_copyl
+go_quad_matrix_determinant
+go_quad_matrix_determinantl
+go_quad_matrix_dump
+go_quad_matrix_dumpl
+go_quad_matrix_dup
+go_quad_matrix_dupl
+go_quad_matrix_eigen_range
+go_quad_matrix_eigen_rangel
+go_quad_matrix_free
+go_quad_matrix_freel
+go_quad_matrix_fwd_solve
+go_quad_matrix_fwd_solvel
+go_quad_matrix_inverse
+go_quad_matrix_inversel
+go_quad_matrix_multiply
+go_quad_matrix_multiplyl
+go_quad_matrix_new
+go_quad_matrix_newl
+go_quad_matrix_pseudo_inverse
+go_quad_matrix_pseudo_inversel
+go_quad_matrix_transpose
+go_quad_matrix_transposel
+<SUBSECTION Private>
+GOQuadMatrix
+GOQuadMatrixl
+</SECTION>
+
+<SECTION>
+<FILE>go-quad-qr</FILE>
+<TITLE>GOQuadQR</TITLE>
+go_quad_qr_determinant
+go_quad_qr_determinantl
+go_quad_qr_free
+go_quad_qr_freel
+go_quad_qr_mark_degenerate
+go_quad_qr_mark_degeneratel
+go_quad_qr_multiply_qt
+go_quad_qr_multiply_qtl
+go_quad_qr_new
+go_quad_qr_newl
+go_quad_qr_r
+go_quad_qr_rl
+<SUBSECTION Private>
+GOQuadQR
+GOQuadQRl
 </SECTION>
 
 <SECTION>
diff --git a/goffice/graph/gog-chart-map.c b/goffice/graph/gog-chart-map.c
index d0a9949..a006b23 100644
--- a/goffice/graph/gog-chart-map.c
+++ b/goffice/graph/gog-chart-map.c
@@ -466,7 +466,7 @@ xy_make_path_step (GogChartMap *map, double const *x, double const *y, int n_poi
 
                        if (n_valid_points == 1)
                                go_path_move_to (path, xx, yy);
-                       else
+                       else {
                                switch (interpolation) {
                                        case GO_LINE_INTERPOLATION_STEP_START:
                                                go_path_line_to (path, xx, last_yy);
@@ -486,7 +486,8 @@ xy_make_path_step (GogChartMap *map, double const *x, double const *y, int n_poi
                                                g_assert_not_reached ();
                                                break;
                                }
-                       go_path_line_to (path, xx, yy);
+                               go_path_line_to (path, xx, yy);
+                       }
                        last_xx = xx;
                        last_yy = yy;
                } else if (!skip_invalid)
diff --git a/goffice/math/go-matrix.c b/goffice/math/go-matrix.c
index 3c3b16d..aa46a0b 100644
--- a/goffice/math/go-matrix.c
+++ b/goffice/math/go-matrix.c
@@ -612,6 +612,7 @@ SUFFIX(go_quad_matrix_dump) (const QMATRIX *A, const char *fmt)
  *
  * R is a matrix of size n-times-n.  (To get the m-times-n version
  * of R, simply add m-n null rows.)
+ * Returns: (transfer full): a new #GOQuadQR.
  **/
 
 /**
diff --git a/goffice/utils/go-path.c b/goffice/utils/go-path.c
index 20e2bf8..6c89e02 100644
--- a/goffice/utils/go-path.c
+++ b/goffice/utils/go-path.c
@@ -346,11 +346,12 @@ static void
 go_path_add_points (GOPath *path, GOPathAction action,
                    GOPathPoint *points, int n_points)
 {
-       GOPathDataBuffer *buffer = path->data_buffer_tail;
+       GOPathDataBuffer *buffer;
        int i;
 
        g_return_if_fail (GO_IS_PATH (path));
 
+       buffer = path->data_buffer_tail;
        if (buffer->n_actions + 1 > GO_PATH_DEFAULT_BUFFER_SIZE
            || buffer->n_points + n_points > GO_PATH_DEFAULT_BUFFER_SIZE)
                buffer = go_path_add_data_buffer (path);
@@ -657,6 +658,153 @@ go_path_interpret (GOPath const           *path,
        }
 }
 
+/**
+ * go_path_interpret_full:
+ * @path: #GOPath
+ * @start: index of the first action to interpret
+ * @end: index of the last action to interpret
+ * @direction: #GOPathDirection
+ * @move_to: (scope call): the callback for move to.
+ * @line_to: (scope call): the callback for drawing a line.
+ * @curve_to: (scope call): the callback for drawing a bezier cubic spline.
+ * @close_path: (scope call): the callback for closing the path.
+ * @closure: data to pass as first argument to the callbacks.
+ *
+ * This function can be used to draw a portion path or for other purposes.
+ * Only actions between start and end will be executed. If start or end is
+ * negative, it is not taken into account.
+ * Since: 0.10.5
+ **/
+void
+go_path_interpret_full (GOPath const           *path,
+                        gssize                          start,
+                        gssize                          end,
+                        GOPathDirection         direction,
+                        GOPathMoveToFunc        move_to,
+                        GOPathLineToFunc        line_to,
+                        GOPathCurveToFunc       curve_to,
+                        GOPathClosePathFunc  close_path,
+                        void                           *closure)
+{
+       GOPathDataBuffer *buffer;
+       GOPathAction action, next_action;
+       GOPathPoint *points;
+       GOPathPoint *prev_control_points = NULL;
+       gboolean forward = (direction == GO_PATH_DIRECTION_FORWARD);
+       int index;
+       gssize cur;
+
+       if (path == NULL || start >= end)
+               return;
+
+       if (forward) {
+               cur = 0;
+               for (buffer = path->data_buffer_head; buffer != NULL; buffer = buffer->next) {
+                       int i;
+                       points = buffer->points;
+
+                       for (i = 0; i != buffer->n_actions; i++) {
+
+                               action = buffer->actions[i];
+
+                               if (end > 0 && cur > end)
+                                       return;
+                               else if (cur == start)
+                                       switch (action) {
+                                       case GO_PATH_ACTION_MOVE_TO:
+                                       case GO_PATH_ACTION_LINE_TO:
+                                               move_to (closure, &points[0]);
+                                               break;
+                                       case GO_PATH_ACTION_CURVE_TO:
+                                               move_to (closure, &points[2]);
+                                               break;
+                                       case GO_PATH_ACTION_CLOSE_PATH:
+                                       default:
+                                               break;
+                                       }
+                               else if (cur > start)
+                                       switch (action) {
+                                       case GO_PATH_ACTION_MOVE_TO:
+                                               move_to (closure, &points[0]);
+                                               break;
+                                       case GO_PATH_ACTION_LINE_TO:
+                                               line_to (closure, &points[0]);
+                                               break;
+                                       case GO_PATH_ACTION_CURVE_TO:
+                                               curve_to (closure, &points[0], &points[1], &points[2]);
+                                               break;
+                                       case GO_PATH_ACTION_CLOSE_PATH:
+                                       default:
+                                               close_path (closure);
+                                               break;
+                                       }
+                               points += action_n_args[action];
+                               cur++;
+                       }
+               }
+               return;
+       }
+
+       next_action = GO_PATH_ACTION_MOVE_TO;
+       cur = 0;
+       for (buffer = path->data_buffer_head; buffer != NULL; buffer = buffer->next)
+               cur += buffer->n_actions;
+
+       for (buffer = path->data_buffer_tail; buffer != NULL; buffer = buffer->previous) {
+               int i;
+
+               points = buffer->points + buffer->n_points;
+
+               for (i = buffer->n_actions - 1; i != -1; i--) {
+                       cur--;
+                       action = next_action;
+                       next_action = buffer->actions[i];
+
+                       points -= action_n_args[next_action];
+
+                       index = next_action == GO_PATH_ACTION_CURVE_TO ? 2 : 0;
+
+                       if (end > 0 && cur > end)
+                               continue;
+                       else if (cur == end)
+                               switch (action) {
+                               case GO_PATH_ACTION_MOVE_TO:
+                               case GO_PATH_ACTION_LINE_TO:
+                               case GO_PATH_ACTION_CURVE_TO:
+                                       (*move_to) (closure, &points[index]);
+                                       break;
+                               case GO_PATH_ACTION_CLOSE_PATH:
+                               default:
+                                       break;
+                               }
+                       else {
+                               switch (action) {
+                               case GO_PATH_ACTION_MOVE_TO:
+                                       (*move_to) (closure, &points[index]);
+                                       break;
+                               case GO_PATH_ACTION_LINE_TO:
+                                       (*line_to) (closure, &points[index]);
+                                       break;
+                               case GO_PATH_ACTION_CURVE_TO:
+                                       (*curve_to) (closure,
+                                                        &prev_control_points[1],
+                                                        &prev_control_points[0],
+                                                        &points[index]);
+                                       break;
+                               case GO_PATH_ACTION_CLOSE_PATH:
+                               default:
+                                       (*close_path) (closure);
+                                       break;
+                               }
+                               if (cur < start)
+                                       return;
+                       }
+
+                       prev_control_points = &points[0];
+               }
+       }
+}
+
 static void
 go_path_cairo_move_to (cairo_t *cr, GOPathPoint const *point)
 {
@@ -729,7 +877,11 @@ go_path_append_close (GOPath *path)
 
 GOPath *go_path_copy (GOPath const *path)
 {
-       GOPath *new_path = go_path_new ();
+       GOPath *new_path;
+
+       if (path == NULL)
+               return NULL;
+       new_path = go_path_new ();
        new_path->options = path->options;
        go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
                           (GOPathMoveToFunc) go_path_append_move_to,
@@ -739,6 +891,33 @@ GOPath *go_path_copy (GOPath const *path)
        return new_path;
 }
 
+
+/**
+ * go_path_copy_restricted:
+ * @path: #GOPath
+ * @start: the first action to copy
+ * @end: the second action to copy
+ *
+ * Copies actions between start and end will be copied inside a new #GOPath.
+ * Returns: a new #GOPath. If start or end is
+ * negative, it is not taken into account.
+ * Since: 0.10.5
+ **/
+
+GOPath *go_path_copy_restricted (GOPath const *path, gssize start, gssize end)
+{
+       GOPath *new_path;
+       if (path == NULL)
+               return NULL;
+       new_path = go_path_new ();
+       new_path->options = path->options;
+       go_path_interpret_full (path, start, end, GO_PATH_DIRECTION_FORWARD,
+                          (GOPathMoveToFunc) go_path_append_move_to,
+                          (GOPathLineToFunc) go_path_append_line_to,
+                          (GOPathCurveToFunc) go_path_append_curve_to,
+                          (GOPathClosePathFunc) go_path_append_close, new_path);
+       return new_path;
+}
 /**
  * go_path_append:
  * @path1: #GOPath
@@ -749,6 +928,10 @@ GOPath *go_path_copy (GOPath const *path)
  */
 GOPath *go_path_append (GOPath *path1, GOPath const *path2)
 {
+       if (path2 == NULL)
+               return path1;
+       if (path1 == NULL)
+               return go_path_copy (path2);
        go_path_interpret (path2, GO_PATH_DIRECTION_FORWARD,
                           (GOPathMoveToFunc) go_path_append_move_to,
                           (GOPathLineToFunc) go_path_append_line_to,
diff --git a/goffice/utils/go-path.h b/goffice/utils/go-path.h
index fe0f5f1..36b955b 100644
--- a/goffice/utils/go-path.h
+++ b/goffice/utils/go-path.h
@@ -105,12 +105,21 @@ void      go_path_interpret       (GOPath const *path,
                                 GOPathCurveToFunc curve_to,
                                 GOPathClosePathFunc close_path,
                                 void *closure);
+void   go_path_interpret_full (GOPath const *path,
+                    gssize start, gssize end,
+                                GOPathDirection direction,
+                                GOPathMoveToFunc move_to,
+                                GOPathLineToFunc line_to,
+                                GOPathCurveToFunc curve_to,
+                                GOPathClosePathFunc close_path,
+                                void *closure);
 
 void    go_path_to_cairo       (GOPath const *path,
                                 GOPathDirection direction,
                                 cairo_t *cr);
 
 GOPath *go_path_copy           (GOPath const *path);
+GOPath *go_path_copy_restricted        (GOPath const *path, gssize start, gssize end);
 GOPath *go_path_append         (GOPath *path1, GOPath const *path2);
 GOPath *go_path_scale       (GOPath *path, double scale_x, double scale_y);
 char   *go_path_to_svg      (GOPath *path);
diff --git a/plugins/plot_barcol/gog-line.c b/plugins/plot_barcol/gog-line.c
index d2252ad..e7ed577 100644
--- a/plugins/plot_barcol/gog-line.c
+++ b/plugins/plot_barcol/gog-line.c
@@ -693,12 +693,214 @@ typedef struct {
 typedef GogPlotView            GogLineView;
 typedef GogPlotViewClass       GogLineViewClass;
 
+static int
+gog_line_view_get_data_at_point (GogPlotView *view, double x, double y, GogSeries **series)
+{
+       GogPlot1_5d const *model = GOG_PLOT1_5D (((GogView *) view)->model);
+       GogPlot1_5dType const type = model->type;
+       GogChart *chart = GOG_CHART (view->base.model->parent);
+       GogChartMap *chart_map;
+       GogAxisMap *x_map, *y_map;
+       int i = -1, j, dist, max_dist = 0, line_dist = 0;
+       GOStyle *style;
+       GogViewAllocation const *area;
+       double xc, yc, value, y_zero;
+       GSList *ptr;
+       GogSeriesElement *gse;
+       GList *overrides; /* not const because we call g_list_* which have no const equivalent */
+       gssize num_series = model->num_series, num_elements = model->num_elements, *lengths;
+       double **vals, **yvals, *xvals, sum, abs_sum;
+       GogSeries **pseries;
+       gboolean is_null;
+       GOLineInterpolation *interpolations;
+
+       if (g_slist_length (model->base.series) < 1)
+               return -1;
+       area = gog_chart_view_get_plot_area (view->base.parent);
+       chart_map = gog_chart_map_new (chart, area,
+                                      GOG_PLOT (model)->axis[GOG_AXIS_X],
+                                      GOG_PLOT (model)->axis[GOG_AXIS_Y],
+                                      NULL, FALSE);
+       if (!gog_chart_map_is_valid (chart_map)) {
+               gog_chart_map_free (chart_map);
+               return -1;
+       }
+
+       x_map = gog_chart_map_get_axis_map (chart_map, 0);
+       y_map = gog_chart_map_get_axis_map (chart_map, 1);
+       y_zero = gog_axis_map_get_baseline (y_map);
+
+       vals    = g_alloca (num_series * sizeof (double *));
+       yvals   = g_alloca (num_series * sizeof (double *));
+       lengths = g_alloca (num_series * sizeof (gssize));
+       pseries = g_alloca (num_series * sizeof (GogSeries *));
+       interpolations  = g_alloca (num_series * sizeof (GOLineInterpolation));
+       xvals = g_new (double, num_elements);
+       j = 0;
+       for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
+               pseries[j] = ptr->data;
+
+               if (!gog_series_is_valid (pseries[j])) {
+                       vals[j] = NULL;
+                       lengths[j] = 0;
+                       continue;
+               }
+
+               vals[j] = go_data_get_values (pseries[j]->values[1].data);
+               yvals[j] = g_new (double, num_elements);
+               lengths[j] = go_data_get_vector_size (pseries[j]->values[1].data);
+               interpolations[j] = pseries[j]->interpolation;
+               if (interpolations[j] == GO_LINE_INTERPOLATION_CLOSED_SPLINE)
+                       interpolations[j] = GO_LINE_INTERPOLATION_SPLINE;
+               j++;
+       }
+       for (i = 0; i < num_elements; i++) {
+               xvals[i] = gog_axis_map_to_view (x_map, i + 1);
+               sum = abs_sum = 0.0;
+               if (type == GOG_1_5D_AS_PERCENTAGE) {
+                       for (j = 0; j < num_series; j++)
+                               if (vals[j]  && i < lengths[j] && gog_axis_map_finite (y_map, vals[j][i]))
+                                       abs_sum += fabs (vals[j][i]);
+                       is_null = (go_sub_epsilon (abs_sum) <= 0.);
+               } else
+                       is_null = TRUE;
+
+               for (j = 0; j < num_series; j++) {
+                       if (i >= lengths[j])
+                               continue;
+
+                       if (vals[j] && gog_axis_map_finite (y_map, vals[j][i])) {
+                               value = vals[j][i];
+                               sum += value;
+                       } else
+                               value = go_nan;
+
+                       switch (type) {
+                               case GOG_1_5D_NORMAL:
+                                       yvals[j][i] = gog_axis_map_finite (y_map, value)?
+                                                                 value: go_nan;
+                                       break;
+
+                               case GOG_1_5D_STACKED:
+                                       yvals[j][i] = gog_axis_map_finite (y_map, sum)?
+                                                                 sum: go_nan;
+                                       break;
+
+                               case GOG_1_5D_AS_PERCENTAGE:
+                                       if (!go_finite (value))
+                                               yvals[j][i] = go_nan;
+                                       else if (is_null)
+                                               yvals[j][i] = y_zero;
+                                       else
+                                       yvals[j][i] = gog_axis_map_finite (y_map, sum)?
+                                                                 sum  / abs_sum:
+                                                                 go_nan;
+                                       break;
+                       }
+               }
+       }
+       if (GOG_IS_PLOT_AREA (model)) {
+               cairo_surface_t *surface;
+               cairo_t *cr;
+               GOPath *path;
+               int start, end, step;
+
+               surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1 , 1);
+               cr = cairo_create (surface);
+               i = -1;
+               if (type == GOG_1_5D_NORMAL) {
+                       start = num_series - 1;
+                       end = -1;
+                       step = -1;
+               } else {
+                       start = 0;
+                       end = num_series;
+                       step = 1;
+               }
+               for (j = start; j != end; j += step) {
+                       if (lengths[j] <= 0)
+                               continue;
+                       path = gog_chart_map_make_path (chart_map, NULL, yvals[j], lengths[j],
+                                                           interpolations[j], FALSE,
+                                                           &GOG_AREA_SERIES (pseries[j])->clamped_derivs);
+                       
+                       go_path_line_to (path, gog_axis_map_to_view (x_map, lengths[j]), y_zero);
+                       go_path_line_to (path, gog_axis_map_to_view (x_map, 1), y_zero);
+                       go_path_close (path);
+                       go_path_to_cairo (path, GO_PATH_DIRECTION_FORWARD, cr);
+                       go_path_free (path);
+                       if (cairo_in_fill (cr, x, y)) {
+                               *series = pseries[j];
+                               i = gog_axis_map_from_view (x_map, x) - 1;
+                               break;
+                       }
+                       cairo_new_path (cr);
+               }
+               cairo_surface_destroy (surface);
+               cairo_destroy (cr);
+       } else { /* GogLinePlot */
+               for (j = num_series -1; j >= 0; j--) {
+                       if (lengths[j] == 0)
+                               continue;
+                       style = go_styled_object_get_style (GO_STYLED_OBJECT (pseries[j]));
+                       if (go_style_is_line_visible (style))
+                               line_dist = ceil (style->line.width / 2);
+                       if (go_style_is_marker_visible (style))
+                               max_dist = (go_marker_get_size (style->marker.mark) + 1) / 2;
+                       else if (go_style_is_line_visible (style))
+                               max_dist = line_dist;
+                       else
+                               max_dist = 0;
+                       overrides = g_list_last ((GList *) gog_series_get_overrides (GOG_SERIES 
(pseries[j])));
+                       for (i = lengths[j] - 1; i >= 0; i--) {
+                               xc = xvals[i];
+                               yc = gog_axis_map_to_view (y_map, yvals[j][i]);
+                               if (!go_finite (xc) || !go_finite (yc))
+                                       continue;
+                               xc = fabs (xc - x);
+                               yc = fabs (yc - y);
+                               dist = MAX (xc, yc);
+                               gse = NULL;
+                               while (overrides &&
+                                      GOG_SERIES_ELEMENT (overrides->data)->index > (unsigned) i)
+                                       overrides = g_list_previous (overrides);
+                               if (overrides &&
+                                   GOG_SERIES_ELEMENT (overrides->data)->index == (unsigned) i) {
+                                       gse = GOG_SERIES_ELEMENT (overrides->data);
+                                       overrides = g_list_previous (overrides);
+                                       style = go_styled_object_get_style (GO_STYLED_OBJECT (gse));
+                                       if (go_style_is_marker_visible (style)) {
+                                               if (dist <= (go_marker_get_size (style->marker.mark) + 1) / 
2) {
+                                                       *series = pseries[j];
+                                                       break;
+                                               }
+                                       } else if (dist <= line_dist) {
+                                               *series = pseries[j];
+                                               break;
+                                       }
+                               }
+                               if (gse == NULL && dist <= max_dist) {
+                                       *series = pseries[j];
+                                       break;
+                               }
+                       }
+                       if (i >= 0)
+                               break;
+               }
+               for (j = 0; j < num_series; j++)
+                       g_free (yvals[j]); 
+       }
+
+       gog_chart_map_free (chart_map);
+       return i;
+}
+
 static void
 gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
 {
        GogPlot1_5d const *model = GOG_PLOT1_5D (view->model);
        GogPlot1_5dType const type = model->type;
-       GogSeries1_5d const *series;
+       GogSeries1_5d const **series;
        GogSeries const *base_series;
        GogChart *chart = GOG_CHART (view->model->parent);
        GogChartMap *chart_map;
@@ -713,7 +915,6 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
        ErrorBarData **error_data;
        GOStyle **styles;
        unsigned *lengths;
-       GOPath *path, *last_path = NULL;
        GOPath **drop_paths;
        Point **points = NULL;
        GogErrorBar **errors;
@@ -764,6 +965,7 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
        error_data = g_alloca (num_series * sizeof (ErrorBarData *));
        lengths = g_alloca (num_series * sizeof (unsigned));
        styles  = g_alloca (num_series * sizeof (GOStyle *));
+       series  = g_alloca (num_series * sizeof (GogSeries *));
        interpolations  = g_alloca (num_series * sizeof (GOLineInterpolation));
        if (!is_area_plot)
                points  = g_alloca (num_series * sizeof (Point *));
@@ -773,8 +975,8 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
 
        i = 0;
        for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
-               series = ptr->data;
-               base_series = GOG_SERIES (series);
+               series[i] = ptr->data;
+               base_series = GOG_SERIES (ptr->data);
 
                if (!gog_series_is_valid (base_series)) {
                        vals[i] = NULL;
@@ -785,7 +987,7 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
                vals[i] = go_data_get_values (base_series->values[1].data);
                yvals[i] = g_new (double, num_elements);
                lengths[i] = go_data_get_vector_size (base_series->values[1].data);
-               styles[i] = GOG_STYLED_OBJECT (series)->style;
+               styles[i] = GOG_STYLED_OBJECT (base_series)->style;
                interpolations[i] = base_series->interpolation;
                if (interpolations[i] == GO_LINE_INTERPOLATION_CLOSED_SPLINE)
                        interpolations[i] = GO_LINE_INTERPOLATION_SPLINE;
@@ -793,12 +995,12 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
                if (!is_area_plot)
                        points[i] = g_malloc (sizeof (Point) * (lengths[i]));
 
-               errors[i] = series->errors;
-               if (gog_error_bar_is_visible (series->errors))
-                       error_data[i] = g_malloc (sizeof (ErrorBarData) * lengths[i]);
+               errors[i] = series[i]->errors;
+               if (gog_error_bar_is_visible (series[i]->errors))
+                       error_data[i] = g_malloc (sizeof (ErrorBarData) * num_elements);
                else
                        error_data[i] = NULL;
-               if (series->has_drop_lines) {
+               if (series[i]->has_drop_lines) {
                        if (!role)
                                role = gog_object_find_role_by_name (
                                                        GOG_OBJECT (series), "Drop lines");
@@ -821,10 +1023,10 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
                        is_null = TRUE;
 
                for (i = 0; i < num_series; i++) {
-                       if (j >= lengths[i])
+                       if (type == GOG_1_5D_NORMAL && j >= lengths[i])
                                continue;
 
-                       if (vals[i] && gog_axis_map_finite (y_map, vals[i][j])) {
+                       if (vals[i] && gog_axis_map_finite (y_map, (j < lengths[i])? vals[i][j]: go_nan)) {
                                value = vals[i][j];
                                if (gog_error_bar_is_visible (errors[i])) {
                                        gog_error_bar_get_bounds (errors[i], j, &minus, &plus);
@@ -925,38 +1127,100 @@ gog_line_view_render (GogView *view, GogViewAllocation const *bbox)
        gog_renderer_push_clip_rectangle (view->renderer, view->allocation.x, view->allocation.y,
                                          view->allocation.w, view->allocation.h);
 
-       for (i = 0; i < num_series; i++) {
-               if (lengths[i] == 0)
-                       continue;
-               path = gog_chart_map_make_path (chart_map, NULL, yvals[i], lengths[i],
-                                               interpolations[i], type != GOG_1_5D_NORMAL && !is_area_plot,
-                                               &GOG_AREA_SERIES (series)->clamped_derivs);
+       if (is_area_plot && type != GOG_1_5D_NORMAL) {
+               GOPath **paths = g_alloca (num_series * sizeof (GOPath *)), *close_path, *seg;
+               unsigned step;
+               unsigned k, m;
+               for (i = 0; i < num_series; i++) {
+                       if (lengths[i] == 0)
+                               continue;
+                       paths[i] = gog_chart_map_make_path (chart_map, NULL, yvals[i], lengths[i],
+                                                           interpolations[i], type != GOG_1_5D_NORMAL && 
!is_area_plot,
+                                                           &GOG_AREA_SERIES (series[i])->clamped_derivs);
+                       close_path = NULL;
+                       gog_renderer_push_style (view->renderer, styles[i]);
+                       j = i;
+                       k = 0;
+                       while (j > 0) {
+                               j--;
+                               if (lengths[j] == 0)
+                                       continue;
+                               if (lengths[j] == lengths[i]) {
+                                       k = lengths[i];
+                                       close_path = paths[j];
+                               } else
+                                       j++;
+                               break;
+                       }
+                       while (j > 0 && k < lengths[i] - 1) {
+                               j--;
+                               if (lengths[j] > k + 1) {
+                                       m = MIN (lengths[i], lengths[j]) - 1;
+                                       switch (interpolations[j]) {
+                                       case GO_LINE_INTERPOLATION_STEP_START:
+                                       case GO_LINE_INTERPOLATION_STEP_END:
+                                               step = 2;
+                                               break;
+                                       case GO_LINE_INTERPOLATION_STEP_CENTER_X:
+                                       case GO_LINE_INTERPOLATION_STEP_CENTER_Y:
+                                               step = 3;
+                                               break;
+                                       default:
+                                               step = 1;
+                                               break;
+                                       }
+                                       seg = go_path_copy_restricted (paths[j], k * step, m * step);
+                                       k = m;
+                                       close_path = go_path_append (close_path, seg);
+                                       go_path_free (seg);
+                               }
+                       }
+                       k++;
+                       if (k < lengths[i]) {
+                               if (close_path == NULL)
+                                       close_path = go_path_new ();
+                               go_path_move_to (close_path, gog_axis_map_to_view (x_map, k), y_zero);
+                               go_path_line_to (close_path, gog_axis_map_to_view (x_map, lengths[i]), 
y_zero);
+                       }
+                       gog_renderer_fill_serie (view->renderer, paths[i], close_path);
+                       gog_renderer_stroke_serie (view->renderer, close_path);
+                       if (close_path != paths[j])
+                               go_path_free (close_path);
+                       gog_renderer_stroke_serie (view->renderer, paths[i]);
+                       gog_renderer_pop_style (view->renderer);
+               }
+               for (i = 0; i < num_series; i++) {
+                       if (lengths[i] == 0)
+                               continue;
+                       go_path_free (paths[i]);
+               }
+       } else {
+               GOPath *path;
+               for (i = 0; i < num_series; i++) {
+                       if (lengths[i] == 0)
+                               continue;
+                       path = gog_chart_map_make_path (chart_map, NULL, yvals[i], lengths[i],
+                                                           interpolations[i], type != GOG_1_5D_NORMAL && 
!is_area_plot,
+                                                           &GOG_AREA_SERIES (series[i])->clamped_derivs);
 
-               gog_renderer_push_style (view->renderer, styles[i]);
+                       gog_renderer_push_style (view->renderer, styles[i]);
 
-               if (!is_area_plot) {
-                       gog_renderer_stroke_serie (view->renderer, path);
-                       go_path_free (path);
-               } else {
-                       if (type == GOG_1_5D_NORMAL || last_path == NULL) {
+                       if (!is_area_plot) {
+                               gog_renderer_stroke_serie (view->renderer, path);
+                               go_path_free (path);
+                       } else {
                                GOPath *close_path = go_path_new ();
                                go_path_move_to (close_path, gog_axis_map_to_view (x_map, 1), y_zero);
                                go_path_line_to (close_path, gog_axis_map_to_view (x_map, lengths[i]), 
y_zero);
                                gog_renderer_fill_serie (view->renderer, path, close_path);
                                gog_renderer_stroke_serie (view->renderer, close_path);
                                go_path_free (close_path);
-                       } else {
-                               gog_renderer_fill_serie (view->renderer, path, last_path);
-                               go_path_free (last_path);
+                               gog_renderer_stroke_serie (view->renderer, path);
+                               go_path_free (path);
                        }
-                       gog_renderer_stroke_serie (view->renderer, path);
-                       last_path = path;
+                       gog_renderer_pop_style (view->renderer);
                }
-
-               gog_renderer_pop_style (view->renderer);
-       }
-       if (last_path)
-               go_path_free (last_path);
+}
 
        /*Now draw drop lines */
        for (i = 0; i < num_series; i++)
@@ -1057,6 +1321,7 @@ gog_line_view_class_init (GogViewClass *view_klass)
        line_view_parent_klass = (GogViewClass*) g_type_class_peek_parent (view_klass);
        view_klass->render        = gog_line_view_render;
        view_klass->size_allocate = gog_line_view_size_allocate;
+       ((GogPlotViewClass *) view_klass)->get_data_at_point = gog_line_view_get_data_at_point;
 }
 
 GSF_DYNAMIC_CLASS (GogLineView, gog_line_view,
diff --git a/plugins/plot_xy/gog-xy.c b/plugins/plot_xy/gog-xy.c
index 7fbf915..a4a9062 100644
--- a/plugins/plot_xy/gog-xy.c
+++ b/plugins/plot_xy/gog-xy.c
@@ -897,7 +897,6 @@ static int
 gog_xy_view_get_data_at_point (GogPlotView *view, double x, double y, GogSeries **series)
 {
        Gog2DPlot const *model = GOG_2D_PLOT (view->base.model);
-       unsigned num_series;
        GogChart *chart = GOG_CHART (view->base.model->parent);
        GogChartMap *chart_map;
        GogAxisMap *x_map, *y_map;
@@ -912,8 +911,7 @@ gog_xy_view_get_data_at_point (GogPlotView *view, double x, double y, GogSeries
        GogSeriesElement *gse;
        GList *overrides; /* not const because we call g_list_* which have no const equivalent */
 
-       for (num_series = 0, ptr = model->base.series ; ptr != NULL ; ptr = ptr->next, num_series++);
-       if (num_series < 1)
+       if (g_slist_length (model->base.series) < 1)
                return -1;
 
        area = gog_chart_view_get_plot_area (view->base.parent);


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