[cairo-commit] goocanvas/src demo-paths.c, 1.1, 1.2 goocanvaspathview.c, 1.1, 1.2

Damon Chaplin commit at pdx.freedesktop.org
Tue Apr 11 06:33:21 PDT 2006


Committed by: damon

Update of /cvs/cairo/goocanvas/src
In directory kemper:/tmp/cvs-serv8813/src

Modified Files:
	demo-paths.c goocanvaspathview.c 
Log Message:
2006-04-11  Damon Chaplin  <damon at gnome.org>

	* src/goocanvaspathview.c: finished the elliptical arc and split
	up the create_path() function a bit.



Index: demo-paths.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/demo-paths.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- demo-paths.c	10 Apr 2006 20:30:24 -0000	1.1
+++ demo-paths.c	11 Apr 2006 13:33:19 -0000	1.2
@@ -54,15 +54,38 @@
 			      NULL);
 
   /* Test the elliptical arc commands: Aa. */
+  path = goo_canvas_path_new (root,
+			      "M200,500 h-150 a150,150 0 1,0 150,-150 z",
+			      "fill-color", "red",
+			      "stroke-color", "blue",
+			      "line-width", 5.0,
+			      NULL);
+
+  path = goo_canvas_path_new (root,
+			      "M175,475 v-150 a150,150 0 0,0 -150,150 z",
+			      "fill-color", "yellow",
+			      "stroke-color", "blue",
+			      "line-width", 5.0,
+			      NULL);
 
+  path = goo_canvas_path_new (root,
+			      "M400,600 l 50,-25 "
+			      "a25,25 -30 0,1 50,-25 l 50,-25 "
+			      "a25,50 -30 0,1 50,-25 l 50,-25 "
+			      "a25,75 -30 0,1 50,-25 l 50,-25 "
+			      "a25,100 -30 0,1 50,-25 l 50,-25",
+			      "stroke-color", "red",
+			      "line-width", 5.0,
+			      NULL);
 }
 
 
 GtkWidget *
 create_paths_page (void)
 {
-  GtkWidget *vbox, *alignment, *frame, *label, *canvas;
+  GtkWidget *vbox, *alignment, *frame, *label, *canvas, *scrolled_win;
   GooCanvasModelSimple *canvas_model;
+  GtkAdjustment *hadj, *vadj;
 
   vbox = gtk_vbox_new (FALSE, 4);
   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
@@ -90,10 +113,17 @@
   canvas_model = goo_canvas_model_simple_new ();
 
   gtk_widget_set_size_request (canvas, 600, 450);
-  goo_canvas_view_set_bounds (GOO_CANVAS_VIEW (canvas), 0, 0, 600, 450);
-  gtk_container_add (GTK_CONTAINER (frame), canvas);
+  goo_canvas_view_set_bounds (GOO_CANVAS_VIEW (canvas), 0, 0, 1000, 1000);
   gtk_widget_show (canvas);
 
+  hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 1000, 10, 100, 100));
+  vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, 1000, 10, 100, 100));
+  scrolled_win = gtk_scrolled_window_new (hadj, vadj);
+  gtk_widget_show (scrolled_win);
+  gtk_container_add (GTK_CONTAINER (frame), scrolled_win);
+
+  gtk_container_add (GTK_CONTAINER (scrolled_win), canvas);
+
   create_paths (canvas_model);
 
   goo_canvas_view_set_model (GOO_CANVAS_VIEW (canvas),

Index: goocanvaspathview.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvaspathview.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- goocanvaspathview.c	10 Apr 2006 20:30:24 -0000	1.1
+++ goocanvaspathview.c	11 Apr 2006 13:33:19 -0000	1.2
@@ -5,6 +5,7 @@
  * goocanvaspathview.c - view for path item.
  */
 #include <config.h>
+#include <math.h>
 #include <gtk/gtk.h>
 #include "goocanvasview.h"
 #include "goocanvaspathview.h"
@@ -67,14 +68,328 @@
 
 
 static void
+do_curve_to (GooCanvasPathCommand *cmd,
+	     cairo_t              *cr,
+	     gdouble              *x,
+	     gdouble              *y,
+	     gdouble              *last_control_point_x,
+	     gdouble              *last_control_point_y)
+{
+  if (cmd->curve.relative)
+    {
+      cairo_curve_to (cr, *x + cmd->curve.x1, *y + cmd->curve.y1,
+		      *x + cmd->curve.x2, *y + cmd->curve.y2,
+		      *x + cmd->curve.x, *y + cmd->curve.y);
+      *last_control_point_x = *x + cmd->curve.x2;
+      *last_control_point_y = *y + cmd->curve.y2;
+      *x += cmd->curve.x;
+      *y += cmd->curve.y;
+    }
+  else
+    {
+      cairo_curve_to (cr, cmd->curve.x1, cmd->curve.y1,
+		      cmd->curve.x2, cmd->curve.y2,
+		      cmd->curve.x, cmd->curve.y);
+      *last_control_point_x = cmd->curve.x2;
+      *last_control_point_y = cmd->curve.y2;
+      *x = cmd->curve.x;
+      *y = cmd->curve.y;
+    }
+}
+
+
+static void
+do_smooth_curve_to (GooCanvasPathCommand    *cmd,
+		    GooCanvasPathCommandType prev_cmd_type,
+		    cairo_t                 *cr,
+		    gdouble                 *x,
+		    gdouble                 *y,
+		    gdouble                 *last_control_point_x,
+		    gdouble                 *last_control_point_y)
+{
+  gdouble x1, y1;
+
+  /* If the last command was a curveto or smooth curveto, we use the
+     reflection of the last control point about the current point as
+     the first control point of this curve. Otherwise we use the
+     current point as the first control point. */
+  if (prev_cmd_type == GOO_CANVAS_PATH_CURVE_TO
+      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_CURVE_TO)
+    {
+      x1 = *x + (*x - *last_control_point_x);
+      y1 = *y + (*y - *last_control_point_y);
+    }
+  else
+    {
+      x1 = *x;
+      y1 = *y;
+    }
+
+  if (cmd->curve.relative)
+    {
+      cairo_curve_to (cr, x1, y1, *x + cmd->curve.x2, *y + cmd->curve.y2,
+		      *x + cmd->curve.x, *y + cmd->curve.y);
+      *last_control_point_x = *x + cmd->curve.x2;
+      *last_control_point_y = *y + cmd->curve.y2;
+      *x += cmd->curve.x;
+      *y += cmd->curve.y;
+    }
+  else
+    {
+      cairo_curve_to (cr, x1, y1, cmd->curve.x2, cmd->curve.y2,
+		      cmd->curve.x, cmd->curve.y);
+      *last_control_point_x = cmd->curve.x2;
+      *last_control_point_y = cmd->curve.y2;
+      *x = cmd->curve.x;
+      *y = cmd->curve.y;
+    }
+}
+
+
+static void
+do_quadratic_curve_to (GooCanvasPathCommand *cmd,
+		       cairo_t              *cr,
+		       gdouble              *x,
+		       gdouble              *y,
+		       gdouble              *last_control_point_x,
+		       gdouble              *last_control_point_y)
+{
+  gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
+
+  if (cmd->curve.relative)
+    {
+      qx1 = *x + cmd->curve.x1;
+      qy1 = *y + cmd->curve.y1;
+      qx2 = *x + cmd->curve.x;
+      qy2 = *y + cmd->curve.y;
+    }
+  else
+    {
+      qx1 = cmd->curve.x1;
+      qy1 = cmd->curve.y1;
+      qx2 = cmd->curve.x;
+      qy2 = cmd->curve.y;
+    }
+
+  /* We need to convert the quadratic into a cubic bezier. */
+  x1 = *x + (qx1 - *x) * 2.0 / 3.0;
+  y1 = *y + (qy1 - *y) * 2.0 / 3.0;
+
+  x2 = x1 + (qx2 - *x) / 3.0;
+  y2 = y1 + (qy2 - *y) / 3.0;
+
+  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
+
+  *x = qx2;
+  *y = qy2;
+  *last_control_point_x = qx1;
+  *last_control_point_y = qy1;
+}
+
+
+static void
+do_smooth_quadratic_curve_to (GooCanvasPathCommand    *cmd,
+			      GooCanvasPathCommandType prev_cmd_type,
+			      cairo_t                 *cr,
+			      gdouble                 *x,
+			      gdouble                 *y,
+			      gdouble                 *last_control_point_x,
+			      gdouble                 *last_control_point_y)
+{
+  gdouble qx1, qy1, qx2, qy2, x1, y1, x2, y2;
+
+  /* If the last command was a quadratic or smooth quadratic, we use
+     the reflection of the last control point about the current point
+     as the first control point of this curve. Otherwise we use the
+     current point as the first control point. */
+  if (prev_cmd_type == GOO_CANVAS_PATH_QUADRATIC_CURVE_TO
+      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO)
+    {
+      qx1 = *x + (*x - *last_control_point_x);
+      qy1 = *y + (*y - *last_control_point_y);
+    }
+  else
+    {
+      qx1 = *x;
+      qy1 = *y;
+    }
+
+  if (cmd->curve.relative)
+    {
+      qx2 = *x + cmd->curve.x;
+      qy2 = *y + cmd->curve.y;
+    }
+  else
+    {
+      qx2 = cmd->curve.x;
+      qy2 = cmd->curve.y;
+    }
+
+  /* We need to convert the quadratic into a cubic bezier. */
+  x1 = *x + (qx1 - *x) * 2.0 / 3.0;
+  y1 = *y + (qy1 - *y) * 2.0 / 3.0;
+
+  x2 = x1 + (qx2 - *x) / 3.0;
+  y2 = y1 + (qy2 - *y) / 3.0;
+
+  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
+
+  *x = qx2;
+  *y = qy2;
+  *last_control_point_x = qx1;
+  *last_control_point_y = qy1;
+}
+
+
+static gdouble
+calc_angle (gdouble ux, gdouble uy, gdouble vx, gdouble vy)
+{
+  gdouble top, u_magnitude, v_magnitude, angle_cos, angle;
+
+  top = ux * vx + uy * vy;
+  u_magnitude = sqrt (ux * ux + uy * uy);
+  v_magnitude = sqrt (vx * vx + vy * vy);
+  angle_cos = top / (u_magnitude * v_magnitude);
+
+  /* We check if the cosine is slightly out-of-bounds. */
+  if (angle_cos >= 1.0)
+    angle = 0.0;
+  if (angle_cos <= -1.0)
+    angle = M_PI;
+  else
+    angle = acos (angle_cos);
+
+  if (ux * vy - uy * vx < 0)
+    angle = - angle;
+
+  return angle;
+}
+
+
+static void
+do_elliptical_arc (GooCanvasPathCommand    *cmd,
+		   cairo_t                 *cr,
+		   gdouble                 *x,
+		   gdouble                 *y)
+{
+  gdouble x1 = *x, y1 = *y, x2, y2, rx, ry, lambda;
+  gdouble v1, v2, angle, angle_sin, angle_cos, x11, y11;
+  gdouble rx_squared, ry_squared, x11_squared, y11_squared, top, bottom;
+  gdouble c, cx1, cy1, cx, cy, start_angle, angle_delta;
+
+  /* Calculate the end point of the arc - x2,y2. */
+  if (cmd->arc.relative)
+    {
+      x2 = x1 + cmd->arc.x;
+      y2 = y1 + cmd->arc.y;
+    }
+  else
+    {
+      x2 = cmd->arc.x;
+      y2 = cmd->arc.y;
+    }
+
+  *x = x2;
+  *y = y2;
+
+  /* If the endpoints are exactly the same, just return (see SVG spec). */
+  if (x1 == x2 && y1 == y2)
+    return;
+
+  /* If either rx or ry is 0, do a simple lineto (see SVG spec). */
+  if (cmd->arc.rx == 0.0 || cmd->arc.ry == 0.0)
+    {
+      cairo_line_to (cr, x2, y2);
+      return;
+    }
+
+  /* Calculate x1' and y1' (as per SVG implementation notes). */
+  v1 = (x1 - x2) / 2.0;
+  v2 = (y1 - y2) / 2.0;
+
+  angle = cmd->arc.x_axis_rotation * (M_PI / 180.0);
+  angle_sin = sin (angle);
+  angle_cos = cos (angle);
+
+  x11 = (angle_cos * v1) + (angle_sin * v2);
+  y11 = - (angle_sin * v1) + (angle_cos * v2);
+
+  /* Ensure rx and ry are positive and large enough. */
+  rx = cmd->arc.rx > 0.0 ? cmd->arc.rx : - cmd->arc.rx;
+  ry = cmd->arc.ry > 0.0 ? cmd->arc.ry : - cmd->arc.ry;
+  lambda = (x11 * x11) / (rx * rx) + (y11 * y11) / (ry * ry);
+  if (lambda > 1.0)
+    {
+      gdouble square_root = sqrt (lambda);
+      rx *= square_root;
+      ry *= square_root;
+    }
+
+  /* Calculate cx' and cy'. */
+  rx_squared = rx * rx;
+  ry_squared = ry * ry;
+  x11_squared = x11 * x11;
+  y11_squared = y11 * y11;
+
+  top = (rx_squared * ry_squared) - (rx_squared * y11_squared)
+    - (ry_squared * x11_squared);
+  if (top < 0.0)
+    {
+      c = 0.0;
+    }
+  else
+    {
+      bottom = (rx_squared * y11_squared) + (ry_squared * x11_squared);
+      c = sqrt (top / bottom);
+    }
+
+  if (cmd->arc.large_arc_flag == cmd->arc.sweep_flag)
+    c = - c;
+
+  cx1 = c * ((rx * y11) / ry);
+  cy1 = c * (- (ry * x11) / rx);
+
+  /* Calculate cx and cy. */
+  cx = (angle_cos * cx1) - (angle_sin * cy1) + (x1 + x2) / 2;
+  cy = (angle_sin * cx1) + (angle_cos * cy1) + (y1 + y2) / 2;
+
+  /* Calculate the start and end angles. */
+  v1 = (x11 - cx1) / rx;
+  v2 = (y11 - cy1) / ry;
+
+  start_angle = calc_angle (1, 0, v1, v2);
+  angle_delta = calc_angle (v1, v2, (-x11 - cx1) / rx, (-y11 - cy1) / ry);
+
+  if (cmd->arc.sweep_flag == 0 && angle_delta > 0.0)
+    angle_delta -= 2 * M_PI;
+  else if (cmd->arc.sweep_flag == 1 && angle_delta < 0.0)
+    angle_delta += 2 * M_PI;
+
+  /* Now draw the arc. */
+  cairo_save (cr);
+  cairo_translate (cr, cx, cy);
+  cairo_rotate (cr, angle);
+  cairo_scale (cr, rx, ry);
+
+  if (angle_delta > 0.0)
+    cairo_arc (cr, 0.0, 0.0, 1.0,
+	       start_angle, start_angle + angle_delta);
+  else
+    cairo_arc_negative (cr, 0.0, 0.0, 1.0,
+			start_angle, start_angle + angle_delta);
+
+  cairo_restore (cr);
+}
+
+
+static void
 goo_canvas_path_view_create_path (GooCanvasPath *path,
 				  cairo_t       *cr)
 {
   GooCanvasPathCommand *cmd;
   GooCanvasPathCommandType prev_cmd_type = GOO_CANVAS_PATH_CLOSE_PATH;
   gdouble x = 0, y = 0, path_start_x = 0, path_start_y = 0;
-  gdouble last_control_point_x, last_control_point_y, x1, y1, x2, y2;
-  gdouble qx1, qy1, qx2, qy2;
+  gdouble last_control_point_x, last_control_point_y;
   gint i;
 
   cairo_new_path (cr);
@@ -142,174 +457,29 @@
 
 	  /* Bezier curve commands: CcSsQqTt. */
 	case GOO_CANVAS_PATH_CURVE_TO:
-	  if (cmd->curve.relative)
-	    {
-	      cairo_curve_to (cr, x + cmd->curve.x1, y + cmd->curve.y1,
-			      x + cmd->curve.x2, y + cmd->curve.y2,
-			      x + cmd->curve.x, y + cmd->curve.y);
-	      last_control_point_x = x + cmd->curve.x2;
-	      last_control_point_y = y + cmd->curve.y2;
-	      x += cmd->curve.x;
-	      y += cmd->curve.y;
-	    }
-	  else
-	    {
-	      cairo_curve_to (cr, cmd->curve.x1, cmd->curve.y1,
-			      cmd->curve.x2, cmd->curve.y2,
-			      cmd->curve.x, cmd->curve.y);
-	      last_control_point_x = cmd->curve.x2;
-	      last_control_point_y = cmd->curve.y2;
-	      x = cmd->curve.x;
-	      y = cmd->curve.y;
-	    }
+	  do_curve_to (cmd, cr, &x, &y,
+		       &last_control_point_x, &last_control_point_y);
 	  break;
 
 	case GOO_CANVAS_PATH_SMOOTH_CURVE_TO:
-	  /* If the last command was a curveto or smooth curveto, we use the
-	     reflection of the last control point about the current point as
-	     the first control point of this curve. Otherwise we use the
-	     current point as the first control point. */
-	  if (prev_cmd_type == GOO_CANVAS_PATH_CURVE_TO
-	      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_CURVE_TO)
-	    {
-	      x1 = x + (x - last_control_point_x);
-	      y1 = y + (y - last_control_point_y);
-	    }
-	  else
-	    {
-	      x1 = x;
-	      y1 = y;
-	    }
-
-	  if (cmd->curve.relative)
-	    {
-	      cairo_curve_to (cr, x1, y1, x + cmd->curve.x2, y + cmd->curve.y2,
-			      x + cmd->curve.x, y + cmd->curve.y);
-	      last_control_point_x = x + cmd->curve.x2;
-	      last_control_point_y = y + cmd->curve.y2;
-	      x += cmd->curve.x;
-	      y += cmd->curve.y;
-	    }
-	  else
-	    {
-	      cairo_curve_to (cr, x1, y1, cmd->curve.x2, cmd->curve.y2,
-			      cmd->curve.x, cmd->curve.y);
-	      last_control_point_x = cmd->curve.x2;
-	      last_control_point_y = cmd->curve.y2;
-	      x = cmd->curve.x;
-	      y = cmd->curve.y;
-	    }
+	  do_smooth_curve_to (cmd, prev_cmd_type, cr, &x, &y,
+			      &last_control_point_x, &last_control_point_y);
 	  break;
 
 	case GOO_CANVAS_PATH_QUADRATIC_CURVE_TO:
-	  if (cmd->curve.relative)
-	    {
-	      qx1 = x + cmd->curve.x1;
-	      qy1 = y + cmd->curve.y1;
-	      qx2 = x + cmd->curve.x;
-	      qy2 = y + cmd->curve.y;
-	    }
-	  else
-	    {
-	      qx1 = cmd->curve.x1;
-	      qy1 = cmd->curve.y1;
-	      qx2 = cmd->curve.x;
-	      qy2 = cmd->curve.y;
-	    }
-
-	  /* We need to convert the quadratic into a cubic bezier. */
-	  x1 = x + (qx1 - x) * 2.0 / 3.0;
-	  y1 = y + (qy1 - y) * 2.0 / 3.0;
-
-	  x2 = x1 + (qx2 - x) / 3.0;
-	  y2 = y1 + (qy2 - y) / 3.0;
-
-	  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
-
-	  x = qx2;
-	  y = qy2;
-	  last_control_point_x = qx1;
-	  last_control_point_y = qy1;
+	  do_quadratic_curve_to (cmd, cr, &x, &y,
+				 &last_control_point_x, &last_control_point_y);
 	  break;
 
 	case GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO:
-	  /* If the last command was a quadratic or smooth quadratic, we use
-	     the reflection of the last control point about the current point
-	     as the first control point of this curve. Otherwise we use the
-	     current point as the first control point. */
-	  if (prev_cmd_type == GOO_CANVAS_PATH_QUADRATIC_CURVE_TO
-	      || prev_cmd_type == GOO_CANVAS_PATH_SMOOTH_QUADRATIC_CURVE_TO)
-	    {
-	      qx1 = x + (x - last_control_point_x);
-	      qy1 = y + (y - last_control_point_y);
-	    }
-	  else
-	    {
-	      qx1 = x;
-	      qy1 = y;
-	    }
-
-	  if (cmd->curve.relative)
-	    {
-	      qx2 = x + cmd->curve.x;
-	      qy2 = y + cmd->curve.y;
-	    }
-	  else
-	    {
-	      qx2 = cmd->curve.x;
-	      qy2 = cmd->curve.y;
-	    }
-
-	  /* We need to convert the quadratic into a cubic bezier. */
-	  x1 = x + (qx1 - x) * 2.0 / 3.0;
-	  y1 = y + (qy1 - y) * 2.0 / 3.0;
-
-	  x2 = x1 + (qx2 - x) / 3.0;
-	  y2 = y1 + (qy2 - y) / 3.0;
-
-	  cairo_curve_to (cr, x1, y1, x2, y2, qx2, qy2);
-
-	  x = qx2;
-	  y = qy2;
-	  last_control_point_x = qx1;
-	  last_control_point_y = qy1;
+	  do_smooth_quadratic_curve_to (cmd, prev_cmd_type, cr, &x, &y,
+					&last_control_point_x,
+					&last_control_point_y);
 	  break;
 
 	  /* The elliptical arc commands: Aa. */
 	case GOO_CANVAS_PATH_ELLIPTICAL_ARC:
-	  /* If the endpoints are exactly the same, just return (See SVG). */
-	  if (x == cmd->arc.x && y == cmd->arc.y)
-	    return;
-
-	  /* If either rx or ry is 0, do a simple lineto (See SVG). */
-	  if (cmd->arc.rx == 0.0 || cmd->arc.ry == 0.0)
-	    {
-	      if (cmd->arc.relative)
-		{
-		  x += cmd->arc.x;
-		  y += cmd->arc.y;
-		}
-	      else
-		{
-		  x = cmd->arc.x;
-		  y = cmd->arc.y;
-		}
-	      cairo_line_to (cr, x, y);
-	      return;
-	    }
-
-#if 0
-	  /* Make sure rx & ry are positive. */
-	  rx = cmd->arc.rx > 0 ? cmd->arc.rx : - cmd->arc.rx;
-	  ry = cmd->arc.ry > 0 ? cmd->arc.ry : - cmd->arc.ry;
-
-
-	  /* FIXME: Need to rotate according to x_axis_rotation, and scale
-	     according to rx & ry. */
-
-
-	  /*cairo_arc (cr, xc, yc, r, angle1, angle2);*/
-#endif
+	  do_elliptical_arc (cmd, cr, &x, &y);
 	  break;
 	}
 



More information about the cairo-commit mailing list