[goffice] Implement spline support in polygons.



commit de15b3def9d5136b7f8d741cac1b4bbdb6ebef0d
Author: Jean Brefort <jean brefort normalesup org>
Date:   Tue Mar 2 15:20:56 2010 +0100

    Implement spline support in polygons.

 ChangeLog                    |   10 ++
 goffice/canvas/goc-item.c    |    2 +
 goffice/canvas/goc-polygon.c |  202 ++++++++++++++++++++++--------------------
 goffice/utils/go-bezier.c    |    2 +-
 4 files changed, 119 insertions(+), 97 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index d0798ab..9215b0a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2010-03-02  Jean Brefort  <jean brefort normalesup org>
+
+	* goffice/canvas/goc-item.c (_goc_item_update_bounds): always set
+	bounds, even for transparent items or empty items.
+	* goffice/canvas/goc-polygon.c (goc_polygon_set_property),
+	(goc_polygon_prepare_path), (goc_polygon_update_bounds),
+	(goc_polygon_distance), (goc_polygon_draw),
+	(goc_polygon_class_init): implement spline support.
+	* goffice/utils/go-bezier.c: fix comment.
+
 2010-03-01  Jean Brefort  <jean brefort normalesup org>
 
 	* goffice/canvas/goc-polyline.c (goc_polyline_set_property),
diff --git a/goffice/canvas/goc-item.c b/goffice/canvas/goc-item.c
index 18e2f81..860b3d5 100644
--- a/goffice/canvas/goc-item.c
+++ b/goffice/canvas/goc-item.c
@@ -274,6 +274,8 @@ _goc_item_update_bounds (GocItem *item)
 	GocItemClass *klass = GOC_ITEM_GET_CLASS (item);
 	g_return_if_fail (klass != NULL);
 
+	item->x0 = item->y0 = G_MAXDOUBLE;
+	item->x1 = item->y1 = -G_MAXDOUBLE;
 	if (klass->update_bounds)
 		klass->update_bounds (item);
 	item->cached_bounds = TRUE;
diff --git a/goffice/canvas/goc-polygon.c b/goffice/canvas/goc-polygon.c
index 2312d6b..306b7ee 100644
--- a/goffice/canvas/goc-polygon.c
+++ b/goffice/canvas/goc-polygon.c
@@ -59,9 +59,12 @@ goc_polygon_set_property (GObject *gobject, guint param_id,
 		GocPoints *points = (GocPoints *) g_value_get_boxed (value);
 		polygon->nb_points = points->n;
 		g_free (polygon->points);
-		polygon->points = g_new (GocPoint, points->n);
-		for (i = 0; i < points->n; i++)
-			polygon->points[i] = points->points[i];
+		if (points->n > 0) {
+			polygon->points = g_new (GocPoint, points->n);
+			for (i = 0; i < points->n; i++)
+				polygon->points[i] = points->points[i];
+		} else
+			polygon->points = NULL;
 		break;
 	}
 	case POLYGON_PROP_SPLINE:
@@ -71,8 +74,18 @@ goc_polygon_set_property (GObject *gobject, guint param_id,
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
 		return; /* NOTE : RETURN */
 	}
+	if (polygon->use_spline && polygon->nb_points) {
+		double *x, *y;
+		unsigned i;
+		x = g_alloca (polygon->nb_points * sizeof (double));
+		y = g_alloca (polygon->nb_points * sizeof (double));
+		for (i = 0; i < polygon->nb_points; i++) {
+			x[i] = polygon->points[i].x - polygon->points[0].x;
+			y[i] = polygon->points[i].y - polygon->points[0].y;
+		}
+		g_object_set_data_full (G_OBJECT (polygon), "spline", go_bezier_spline_init (x, y, polygon->nb_points, TRUE), (GDestroyNotify) go_bezier_spline_destroy);
+	}
 	goc_item_bounds_changed (GOC_ITEM (gobject));
-
 }
 
 static void
@@ -100,119 +113,116 @@ goc_polygon_get_property (GObject *gobject, guint param_id,
 	}
 }
 
-static void
-goc_polygon_update_bounds (GocItem *item)
+static gboolean
+goc_polygon_prepare_path (GocItem const *item, cairo_t *cr, gboolean flag)
 {
 	GocPolygon *polygon = GOC_POLYGON (item);
-	GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (item));
-	/* FIXME: extra_width might be not large enough if angles are small */
-	double extra_width = style->line.width;
 	unsigned i;
-	if (extra_width <= 0.)
-		extra_width = 1.;
+
 	if (polygon->nb_points == 0)
-		return;
-	/* FIXME: implement the use_spline case */
-	item->x0 = item->x1 = polygon->points[0].x;
-	item->y0 = item->y1 = polygon->points[0].y;
-	for (i = 1; i < polygon->nb_points; i++) {
-		if (polygon->points[i].x < item->x0)
-			item->x0 = polygon->points[i].x;
-		else if (polygon->points[i].x > item->x1)
-			item->x1 = polygon->points[i].x;
-		if (polygon->points[i].y < item->y0)
-			item->y0 = polygon->points[i].y;
-		else if (polygon->points[i].y > item->y1)
-			item->y1 = polygon->points[i].y;
+		return FALSE;
+
+	if (1 == flag) {
+		goc_group_cairo_transform (item->parent, cr, polygon->points[0].x, polygon->points[0].y);
+		cairo_move_to (cr, 0., 0.);
+	} else {
+		cairo_move_to (cr, polygon->points[0].x, polygon->points[0].y);
+	}
+	if (polygon->use_spline) {
+		GOBezierSpline *spline = (GOBezierSpline *) g_object_get_data (G_OBJECT (polygon), "spline");
+		cairo_save (cr);
+		if (flag == 0)
+			cairo_translate (cr, polygon->points[0].x, polygon->points[0].y);
+		go_bezier_spline_to_cairo (spline, cr, goc_canvas_get_direction (item->canvas) == GOC_DIRECTION_RTL);
+		cairo_restore (cr);
+	} else {
+		double sign = (goc_canvas_get_direction (item->canvas) == GOC_DIRECTION_RTL)? -1: 1;
+		for (i = 1; i < polygon->nb_points; i++)
+			cairo_line_to (cr, (polygon->points[i].x - polygon->points[0].x * flag) * sign,
+				polygon->points[i].y - polygon->points[0].y * flag);
+		cairo_close_path (cr);
 	}
-	item->x0 -= extra_width;
-	item->y0 -= extra_width;
-	item->x1 += extra_width;
-	item->y1 += extra_width;
+	
+	return TRUE;
+}
+
+static void
+goc_polygon_update_bounds (GocItem *item)
+{
+	cairo_surface_t *surface;
+	cairo_t *cr;
+	int mode = 0;
+
+	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+	cr = cairo_create (surface);
+	if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr))
+		mode =1;
+	else if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
+		mode = 2;
+	if (mode && goc_polygon_prepare_path (item, cr, 0)) {
+		if (mode == 1)
+			cairo_stroke_extents (cr, &item->x0, &item->y0, &item->x1, &item->y1);
+		else
+			cairo_fill_extents (cr, &item->x0, &item->y0, &item->x1, &item->y1);
+	}
+	cairo_destroy (cr);
+	cairo_surface_destroy (surface);
 }
 
 static double
 goc_polygon_distance (GocItem *item, double x, double y, GocItem **near_item)
 {
 	GocPolygon *polygon = GOC_POLYGON (item);
-	GOStyle *style = go_styled_object_get_style (GO_STYLED_OBJECT (item));
-	/* FIXME: implement the use_spline case */
-	double extra_width = (style->line.width)? style->line.width /2.: .5;
-	double dx, dy, l, startx, starty, x_, y_, t, res = 0.;
-	int i, n;
+	GOStyle *style = go_style_dup (go_styled_object_get_style (GO_STYLED_OBJECT (item)));
+	double res = G_MAXDOUBLE;
+	cairo_surface_t *surface;
+	cairo_t *cr;
+
+	if (polygon->nb_points == 0)
+		return res;
 
 	*near_item = item;
-	/* FIXME: not tested!!! */
-	/* first test if the point is inside the polygon */
-	startx = atan2 (polygon->points[0].y - y, polygon->points[0].x - x);
-	for (i = polygon->nb_points - 1; i >= 0; i--) {
-		starty = atan2 (polygon->points[i].y - y, polygon->points[i].x - x);
-		startx -= starty;
-		if (startx > M_PI)
-			startx -= 2 * M_PI;
-		else if (startx < -M_PI)
-			startx += 2 * M_PI;
-		else if (startx == M_PI)
-			return 0.; /* the point is on the edge */
-		res += startx;
-		startx = starty;
+	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
+	cr = cairo_create (surface);
+	goc_polygon_prepare_path (item, cr, 0);
+	if (style->fill.type != GO_STYLE_FILL_NONE) {
+		go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr);
+		if (cairo_in_fill (cr, x, y))
+			res = 0;
+		else if ((item->x1 - item->x0 < 5 || item->y1 - item->y0 < 5) && style->line.dash_type == GO_LINE_NONE) {
+			style->line.dash_type = GO_LINE_SOLID;
+			style->line.auto_dash = FALSE;
+			style->line.width = 5;
+		}
 	}
-	n = res / 2. / M_PI;
-	if (n % 1)
-		return 0.; /* only odd-even fillinig supported for now */
-	startx = polygon->points[0].x;
-	starty = polygon->points[0].y;
-	res = G_MAXDOUBLE;
-	for (i = polygon->nb_points - 1; i >= 0; i--) {
-		dx = polygon->points[i].x - startx;
-		dy = polygon->points[i].y - starty;
-		l = hypot (dx, dy);
-		x_ = x - startx;
-		y_ = y - starty;
-		t = (x_ * dx + y_ * dy) / l;
-		y = (-x_ * dy + y_ * dx) / l;
-		x_ = t;
-		*near_item = item;
-		if (x < 0. ) {
-			t = hypot (x_, y_);
-			if (t < res)
-				res = t;
-		} else if (t <= l) {
-			if (y_ < res)
-				res = y_;
+	if (res > 0 && style->line.dash_type != GO_LINE_NONE) {
+		if (style->line.width < 5) {
+			style->line.width = 5;
 		}
-		startx = polygon->points[i].x;
-		starty = polygon->points[i].y;
+		go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr);
+		if (cairo_in_stroke (cr, x, y))
+			res = 0;
 	}
-	res -= extra_width; /* no need to be more precise */
-	return (res > 0.)? res: 0.;
+
+	g_object_unref (style);
+	cairo_destroy (cr);
+	cairo_surface_destroy (surface);
+	return res;
 }
 
 static void
 goc_polygon_draw (GocItem const *item, cairo_t *cr)
 {
-	GocPolygon *polygon = GOC_POLYGON (item);
-	unsigned i;
-	double sign = (goc_canvas_get_direction (item->canvas) == GOC_DIRECTION_RTL)? -1: 1;
-	if (polygon->nb_points == 0)
-		return;
 	cairo_save (cr);
-	goc_group_cairo_transform (item->parent, cr, polygon->points[0].x, polygon->points[0].y);
-        cairo_move_to (cr, 0., 0.);
-	/* FIXME: implement the use_spline case */
-
-	for (i = 1; i < polygon->nb_points; i++)
-		cairo_line_to (cr, (polygon->points[i].x - polygon->points[0].x) * sign,
-		               polygon->points[i].y - polygon->points[0].y);
-	cairo_close_path (cr);
+	if (goc_polygon_prepare_path (item, cr, 1)) {
+		if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
+			cairo_fill_preserve (cr);
 
-	if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
-		cairo_fill_preserve (cr);
-
-	if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr))
-		cairo_stroke (cr);
-	else
-		cairo_new_path (cr);
+		if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr))
+			cairo_stroke (cr);
+		else
+			cairo_new_path (cr);
+	}
 	cairo_restore (cr);
 }
 
@@ -250,12 +260,12 @@ goc_polygon_class_init (GocItemClass *item_klass)
                  g_param_spec_boxed ("points", _("points"), _("The polygon vertices"),
 				     GOC_TYPE_POINTS,
 				     GSF_PARAM_STATIC | G_PARAM_READWRITE));
-/*	g_object_class_install_property (obj_klass, POLYGON_PROP_SPLINE,
+	g_object_class_install_property (obj_klass, POLYGON_PROP_SPLINE,
 		g_param_spec_boolean ("use-spline",
 				      _("Use spline"),
 				      _("Use a Bezier closed cubic spline as contour"),
 				      FALSE,
-				      GSF_PARAM_STATIC | G_PARAM_READABLE));*/
+				      GSF_PARAM_STATIC | G_PARAM_READWRITE));
 
 	item_klass->update_bounds = goc_polygon_update_bounds;
 	item_klass->distance = goc_polygon_distance;
diff --git a/goffice/utils/go-bezier.c b/goffice/utils/go-bezier.c
index c091b57..be8b06b 100644
--- a/goffice/utils/go-bezier.c
+++ b/goffice/utils/go-bezier.c
@@ -337,7 +337,7 @@ go_bezier_spline_to_path (GOBezierSpline *sp)
 }
 
 /**
- * go_bezier_spline_to_path:
+ * go_bezier_spline_to_cairo:
  * @sp: a struct GOBezierSpline instance returned by go_bezier_spline_init
  * @cr: a cairo context
  *



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