[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