[cairo] arc_to patch version 0.0.0

Behdad Esfahbod behdad at cs.toronto.edu
Tue Jul 26 23:18:49 PDT 2005


Hi,

I'm attaching a very draft implementation of arc_to.  Rough
around the edges and no documentation, but I wanted to make sure
I'm on the right track before going on.  The patch actually works
quite good, except for when the arc doesn't fit, that I'm not
sure should be reported as an error or the radius should be
decreased.  Run the demo and you see what I mean.

The patch includes the sincos patch too.  BTW, do you think
_cairo_cossin is a better name than _cairo_sincos?  The reason is
that normally cos() is associated with x and sin() with y...

The arclock.c is a demo program that draws two simple clock hands
connected together using an arc_to.

Cheers,
--behdad
http://behdad.org/
-------------- next part --------------
Index: configure.in
===================================================================
RCS file: /cvs/cairo/cairo/configure.in,v
retrieving revision 1.111
diff -u -p -r1.111 configure.in
--- configure.in	18 Jul 2005 18:51:42 -0000	1.111
+++ configure.in	27 Jul 2005 06:07:24 -0000
@@ -31,20 +31,19 @@ AM_CONFIG_HEADER(config.h)
 
 AM_MAINTAINER_MODE
 
+AC_GNU_SOURCE
 AC_PROG_CC
 AC_PROG_CPP
 AM_PROG_LIBTOOL
 AC_STDC_HEADERS
 AC_C_BIGENDIAN
 
-AC_CHECK_FUNCS(vasnprintf)
-	
+AC_CHECK_FUNCS(vasnprintf, sincos)
+
 AC_CHECK_LIBM
 
 LIBS="$LIBS $LIBM"
 
-AC_CHECK_FUNCS(sincos)
-
 dnl ===========================================================================
 
 AC_ARG_ENABLE(xlib,
Index: src/cairo-arc-private.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-arc-private.h,v
retrieving revision 1.1
diff -u -p -r1.1 cairo-arc-private.h
--- src/cairo-arc-private.h	26 Apr 2005 19:38:06 -0000	1.1
+++ src/cairo-arc-private.h	27 Jul 2005 06:07:24 -0000
@@ -54,4 +54,11 @@ _cairo_arc_path_negative (cairo_t *cr,
 			  double   angle1,
 			  double   angle2);
 
+void
+_cairo_arc_with_handle (cairo_t *cr,
+			double x0, double y0,
+			double x1, double y1,
+			double x2, double y2,
+			double radius);
+
 #endif /* CAIRO_ARC_PRIVATE_H */
Index: src/cairo-arc.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-arc.c,v
retrieving revision 1.3
diff -u -p -r1.3 cairo-arc.c
--- src/cairo-arc.c	11 Jul 2005 20:29:46 -0000	1.3
+++ src/cairo-arc.c	27 Jul 2005 06:07:24 -0000
@@ -61,7 +61,10 @@
 static double
 _arc_error_normalized (double angle)
 {
-    return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2);
+    double sin_, cos_;
+
+    _cairo_sincos (angle / 4, &sin_, &cos_);
+    return 2.0/27.0 * pow (sin_, 6) / pow (cos_, 2);
 }
 
 static double
@@ -164,10 +167,12 @@ _cairo_arc_segment (cairo_t *cr,
     double r_sin_B, r_cos_B;
     double h;
 
-    r_sin_A = radius * sin (angle_A);
-    r_cos_A = radius * cos (angle_A);
-    r_sin_B = radius * sin (angle_B);
-    r_cos_B = radius * cos (angle_B);
+    _cairo_sincos (angle_A, &r_sin_A, &r_cos_A);
+    _cairo_sincos (angle_B, &r_sin_B, &r_cos_B);
+    r_sin_A *= radius;
+    r_cos_A *= radius;
+    r_sin_B *= radius;
+    r_cos_B *= radius;
 
     h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0);
 
@@ -296,3 +301,49 @@ _cairo_arc_path_negative (cairo_t *cr,
 			     angle2, angle1,
 			     CAIRO_DIRECTION_REVERSE);
 }
+
+void
+_cairo_arc_with_handle (cairo_t *cr,
+			double x0, double y0,
+			double x1, double y1,
+			double x2, double y2,
+			double radius)
+{
+    double angle0, angle1, angle2, angled;
+    double xc, yc, dc;
+    cairo_direction_t dir;
+
+    angle0 = atan2 (y0 - y1, x0 - x1);
+    angle2 = atan2 (y2 - y1, x2 - x1);
+    angle1 = (angle0 + angle2) / 2;
+
+    angled = angle2 - angle0;
+
+    if (angled > M_PI || (angled < 0 && angled > -M_PI)) {
+      angle1 += M_PI;
+      angled = 2 * M_PI - angled;
+      angle0 += M_PI_2;
+      angle2 -= M_PI_2;
+      dir = CAIRO_DIRECTION_FORWARD;
+    } else {
+      angle0 -= M_PI_2;
+      angle2 += M_PI_2;
+      dir = CAIRO_DIRECTION_REVERSE;
+    }
+
+    /* distance of center from (x1,y1) */
+    dc = radius / sin (angled / 2);
+
+    _cairo_sincos (angle1, &yc, &xc);
+    xc = x1 + xc * dc;
+    yc = y1 + yc * dc;
+    /* (cx,cy) is the center of arc now */
+
+    if (dir == CAIRO_DIRECTION_FORWARD)
+      cairo_arc (cr, xc, yc, radius, angle0, angle2);
+    else
+      cairo_arc_negative (cr, xc, yc, radius, angle0, angle2);
+
+    cairo_line_to (cr, x2, y2);
+}
+
Index: src/cairo-matrix.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-matrix.c,v
retrieving revision 1.29
diff -u -p -r1.29 cairo-matrix.c
--- src/cairo-matrix.c	20 Jun 2005 19:54:15 -0000	1.29
+++ src/cairo-matrix.c	27 Jul 2005 06:07:24 -0000
@@ -227,17 +227,12 @@ void
 cairo_matrix_init_rotate (cairo_matrix_t *matrix,
 			  double radians)
 {
-    double  s;
-    double  c;
-#if HAVE_SINCOS
-    sincos (radians, &s, &c);
-#else
-    s = sin (radians);
-    c = cos (radians);
-#endif
+    double  sin_, cos_;
+
+    _cairo_sincos (radians, &sin_, &cos_);
     cairo_matrix_init (matrix,
-		       c, s,
-		       -s, c,
+		       cos_, sin_,
+		       -sin_, cos_,
 		       0, 0);
 }
 slim_hidden_def(cairo_matrix_init_rotate);
Index: src/cairo-pattern.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-pattern.c,v
retrieving revision 1.50
diff -u -p -r1.50 cairo-pattern.c
--- src/cairo-pattern.c	14 Jul 2005 22:47:18 -0000	1.50
+++ src/cairo-pattern.c	27 Jul 2005 06:07:25 -0000
@@ -1100,6 +1100,8 @@ _cairo_image_data_set_radial (cairo_radi
 		denumerator = -2.0 * c0_e * c0_c1;
 		
 		if (denumerator != 0.0) {
+		    double sin_, cos_;
+
 		    fraction = (c1_e * c1_e - c0_e * c0_e - c0_c1 * c0_c1) /
 			denumerator;
 
@@ -1110,8 +1112,9 @@ _cairo_image_data_set_radial (cairo_radi
 		    
 		    angle_c0 = acos (fraction);
 		    
-		    c0_y = cos (angle_c0) * c0_c1;
-		    c1_y = sin (angle_c0) * c0_c1;
+		    _cairo_sincos (angle_c0, &sin_, &cos_);
+		    c0_y = cos_ * c0_c1;
+		    c1_y = sin_ * c0_c1;
 		    
 		    y_x = sqrt (r1_2 - c1_y * c1_y);
 		    c0_x = y_x + c0_y;
Index: src/cairo-pen.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo-pen.c,v
retrieving revision 1.23
diff -u -p -r1.23 cairo-pen.c
--- src/cairo-pen.c	7 Apr 2005 17:01:49 -0000	1.23
+++ src/cairo-pen.c	27 Jul 2005 06:07:25 -0000
@@ -102,8 +102,11 @@ _cairo_pen_init (cairo_pen_t *pen, doubl
      */
     for (i=0; i < pen->num_vertices; i++) {
 	double theta = 2 * M_PI * i / (double) pen->num_vertices;
-	double dx = radius * cos (reflect ? -theta : theta);
-	double dy = radius * sin (reflect ? -theta : theta);
+	double dx, dy;
+
+	_cairo_sincos (reflect ? -theta : theta, &dx, &dy);
+	dx *= radius;
+	dy *= radius;
 	cairo_pen_vertex_t *v = &pen->vertices[i];
 	cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy);
 	v->point.x = _cairo_fixed_from_double (dx);
Index: src/cairo.c
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo.c,v
retrieving revision 1.114
diff -u -p -r1.114 cairo.c
--- src/cairo.c	25 Jul 2005 19:29:24 -0000	1.114
+++ src/cairo.c	27 Jul 2005 06:07:25 -0000
@@ -1013,6 +1013,8 @@ cairo_arc (cairo_t *cr,
 	   double radius,
 	   double angle1, double angle2)
 {
+    double sin_, cos_;
+
     if (cr->status) {
 	_cairo_error (cr, cr->status);
 	return;
@@ -1025,9 +1027,11 @@ cairo_arc (cairo_t *cr,
     while (angle2 < angle1)
 	angle2 += 2 * M_PI;
 
+    _cairo_sincos (angle1, &sin_, &cos_);
+
     cairo_line_to (cr,
-		   xc + radius * cos (angle1),
-		   yc + radius * sin (angle1));
+		   xc + radius * cos_,
+		   yc + radius * sin_);
 
     _cairo_arc_path (cr, xc, yc, radius,
 		     angle1, angle2);
@@ -1053,6 +1057,8 @@ cairo_arc_negative (cairo_t *cr,
 		    double radius,
 		    double angle1, double angle2)
 {
+    double sin_, cos_;
+
     if (cr->status) {
 	_cairo_error (cr, cr->status);
 	return;
@@ -1065,30 +1071,37 @@ cairo_arc_negative (cairo_t *cr,
     while (angle2 > angle1)
 	angle2 -= 2 * M_PI;
 
+    _cairo_sincos (angle1, &sin_, &cos_);
+
     cairo_line_to (cr,
-		   xc + radius * cos (angle1),
-		   yc + radius * sin (angle1));
+		   xc + radius * cos_,
+		   yc + radius * sin_);
 
-     _cairo_arc_path_negative (cr, xc, yc, radius,
-			       angle1, angle2);
+    _cairo_arc_path_negative (cr, xc, yc, radius,
+			      angle1, angle2);
 }
 
-/* XXX: NYI
 void
 cairo_arc_to (cairo_t *cr,
 	      double x1, double y1,
 	      double x2, double y2,
 	      double radius)
 {
+    double x0, y0;
+
     if (cr->status)
 	return;
 
-    cr->status = _cairo_gstate_arc_to (cr->gstate,
-				       x1, y1,
-				       x2, y2,
-				       radius);
+    cairo_get_current_point (cr, &x0, &y0);
+
+
+
+    _cairo_arc_with_handle (cr,
+			    x0, y0,
+			    x1, y1,
+			    x2, y2,
+			    radius);
 }
-*/
 
 void
 cairo_rel_move_to (cairo_t *cr, double dx, double dy)
Index: src/cairo.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairo.h,v
retrieving revision 1.139
diff -u -p -r1.139 cairo.h
--- src/cairo.h	25 Jul 2005 19:29:24 -0000	1.139
+++ src/cairo.h	27 Jul 2005 06:07:26 -0000
@@ -412,13 +412,11 @@ cairo_arc_negative (cairo_t *cr,
 		    double radius,
 		    double angle1, double angle2);
 
-/* XXX: NYI
 void
 cairo_arc_to (cairo_t *cr,
 	      double x1, double y1,
 	      double x2, double y2,
 	      double radius);
-*/
 
 void
 cairo_rel_move_to (cairo_t *cr, double dx, double dy);
Index: src/cairoint.h
===================================================================
RCS file: /cvs/cairo/cairo/src/cairoint.h,v
retrieving revision 1.169
diff -u -p -r1.169 cairoint.h
--- src/cairoint.h	25 Jul 2005 23:23:05 -0000	1.169
+++ src/cairoint.h	27 Jul 2005 06:07:26 -0000
@@ -65,6 +65,16 @@
 #include "cairo.h"
 #include <pixman.h>
 
+#if HAVE_SINCOS
+# define _cairo_sincos(angle,s,c) sincos((angle), (s), (c))
+#else
+# define _cairo_sincos(angle,s,c) do { \
+	double _sincos_angle = (angle); \
+	*(s) = sin(_sincos_angle); \
+	*(c) = cos(_sincos_angle); \
+	} while (0)
+#endif
+
 #if __GNUC__ >= 3 && defined(__ELF__)
 # define slim_hidden_proto(name)	slim_hidden_proto1(name, INT_##name)
 # define slim_hidden_def(name)		slim_hidden_def1(name, INT_##name)
-------------- next part --------------
/* arclock.c - yet another clock using cairo with gtk+
 *
 * Copyright ? 2005, Carl Worth
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#define WIDTH 600
#define HEIGHT 600 
#include <cairo.h>
#include <math.h>

static double angle = 1.0;

static void
basket (cairo_t *cr)
{
    cairo_move_to (cr, 300 * (1 + cos(angle)), 300 * (1 + sin(angle)));
    cairo_arc_to (cr, 300, 300, 300 * (1 + .8*cos(angle/60)), 300 * (1 + .8*sin(angle/60)), 50);
}

static void
draw (cairo_t *cr)
{
    angle += 5*M_PI/180;
    cairo_set_line_width (cr, 20);
    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
    cairo_set_source_rgb (cr, .5, .5, .9);
    basket (cr);
    cairo_stroke (cr);

    cairo_set_line_width (cr, 10);
    cairo_set_line_join (cr, CAIRO_LINE_JOIN_BEVEL);
    cairo_set_source_rgb (cr, .9, .1, .3);
    basket (cr);
    cairo_stroke (cr);
}

#include <gtk/gtk.h>
#include <cairo-xlib.h>
#include <gdk/gdkx.h>

static void
draw (cairo_t *cr);

#if ! GTK_CHECK_VERSION(2,7,0)
/* copied from gtk+/gdk/gdkcairo.c and gtk+/gdk/x11/gdkdrawable-x11.c
 * gdk_cairo_create() which is available in 2.7.0 and later.
 */
static cairo_t *
gdk_cairo_create (GdkDrawable *drawable)
{
    int width, height;
    cairo_t *cr = NULL;
    cairo_surface_t *surface = NULL;
    GdkVisual *visual = gdk_drawable_get_visual (drawable);
    GdkDrawable *dr;
    gint x_off, y_off;

    gdk_drawable_get_size (drawable, &width, &height);
    if (visual) {
	gdk_window_get_internal_paint_info(drawable, &dr, &x_off, &y_off);
	surface = cairo_xlib_surface_create (GDK_DRAWABLE_XDISPLAY (drawable),
					     GDK_DRAWABLE_XID (dr),
					     GDK_VISUAL_XVISUAL (visual),
					     width, height);
    }
    else if (gdk_drawable_get_depth (drawable) == 1)
	surface = cairo_xlib_surface_create_for_bitmap 
	    (GDK_PIXMAP_XDISPLAY (drawable),
	     GDK_PIXMAP_XID (drawable),
	     width, height);
    else {
	g_warning ("Using Cairo rendering requires the drawable argument to\n"
		   "have a specified colormap. All windows have a colormap,\n"
		   "however, pixmaps only have colormap by default if they\n"
		   "were created with a non-NULL window argument. Otherwise\n"
		   "a colormap must be set on them with "
		   "gdk_drawable_set_colormap");
	return NULL;
    }
    if (surface) {
	cr = cairo_create (surface);
	cairo_surface_destroy (surface);
    }
    return cr;
}
#endif

static gboolean
handle_expose (GtkWidget      *widget,
	       GdkEventExpose *event,
	       gpointer	       data)
{
    cairo_t *cr;

    cr = gdk_cairo_create (widget->window);

    draw (cr);

    cairo_destroy (cr);

    return FALSE;
}

static void
tick (GtkWidget      *widget)
{
    GdkRectangle update_rect;

    update_rect.x = 0;
    update_rect.y = 0;
    update_rect.width = widget->allocation.width;
    update_rect.height = widget->allocation.height;

    gdk_window_invalidate_rect(widget->window,&update_rect,FALSE);
}

int 
main (int argc, char **argv)
{
    GtkWidget *window, *drawing_area;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    gtk_window_set_default_size (GTK_WINDOW (window), WIDTH, HEIGHT);
    gtk_window_set_title (GTK_WINDOW (window), "cairo demo");

    g_signal_connect (window, "destroy-event",
		      G_CALLBACK (gtk_main_quit), NULL);

    drawing_area = gtk_drawing_area_new ();
    gtk_container_add (GTK_CONTAINER (window), drawing_area);

    g_signal_connect (drawing_area, "expose-event",
		      G_CALLBACK (handle_expose), NULL);

    g_timeout_add (1000, (GSourceFunc) tick, drawing_area);

    gtk_widget_show_all (window);

    gtk_main ();
    
    return 0;
}


More information about the cairo mailing list