diff src/shape_path.c @ 98:688f76b8e71c

Implement arc for path, but it is still under testing
author Thinker K.F. Li <thinker@branda.to>
date Tue, 09 Sep 2008 18:11:37 +0800
parents 9453e68092b5
children 4aa1c9673363
line wrap: on
line diff
--- a/src/shape_path.c	Thu Sep 04 08:21:39 2008 +0800
+++ b/src/shape_path.c	Tue Sep 09 18:11:37 2008 +0800
@@ -19,6 +19,7 @@
     shape_t shape;
     int cmd_len;
     int arg_len;
+    int fix_arg_len;
     char *user_data;
     char *dev_data;		/* device space data */
 } sh_path_t;
@@ -36,6 +37,260 @@
     }
 #define OK 0
 #define ERR -1
+#define PI 3.1415926
+
+/* ============================================================
+ * Implement arc in path.
+ */
+#include <math.h>
+/*! \brief Calculate center of the ellipse of an arc.
+ *
+ * - ux0 = x0 / rx
+ * - uy0 = y0 / ry
+ * - ux = x / rx
+ * - uy = y / rx
+ * ux0, uy0, ux, yu are got by transforming (x0, y0) and (x, y) into points
+ * on the unit circle. The center of unit circle are (ucx, ucy):
+ * - umx = (ux0 + ux) / 2
+ * - umy = (uy0 + uy) / 2
+ * - udcx = ucx - umx
+ * - udcy = ucy - umy
+ * - udx = ux - umx
+ * - udy = uy - umy
+ *
+ * - udcx * udx + udcy * udy = 0
+ *
+ * - udl2 = udx ** 2 + udy ** 2;
+ * - udcx * udy - udcy * udx = sqrt((1 - udl2) * udl2)
+ *
+ * - udcy = -udcx * udx / udy
+ * - udcx * udy + udcx * (udx ** 2) / udy = sqrt((1 - udl2) * udl2)
+ * - udcx * (udy + (udx ** 2) / udy) = sqrt((1 - udl2) * udl2)
+ * - udcx = sqrt((1 - udl2) * udl2) / (udy + (udx ** 2) / udy)
+ * or
+ * - udcx = -udcy * udy / udx
+ * - -udcy * (udy ** 2) / udx - udcy * udx = sqrt((1 - udl2) * udl2)
+ * - -udcy * ((udy ** 2) / udx + udx) = sqrt((1 - udl2) * udl2)
+ * - udcy = -sqrt((1 - udl2) * udl2) / ((udy ** 2) / udx + udx)
+ *
+ * - cx = rx * ucx
+ * - cx = rx * (udcx + umx)
+ * - cy = ry * ucy
+ * - cy = ry * (udcy + umy)
+ */
+static int calc_center_and_x_aix(co_aix x0, co_aix y0,
+				 co_aix x, co_aix y,
+				 co_aix rx, co_aix ry,
+				 co_aix x_rotate,
+				 int large, int sweep,
+				 co_aix *cx, co_aix *cy,
+				 co_aix *xx, co_aix *xy) {
+    co_aix nrx, nry, nrx0, nry0;
+    co_aix udx, udy, udx2, udy2;
+    co_aix umx, umy;
+    co_aix udcx, udcy;
+    co_aix nrcx, nrcy;
+    co_aix udl2;
+    float _sin = sinf(x_rotate);
+    float _cos = cosf(x_rotate);
+    int reflect;
+    
+    nrx = x * _cos + y * _sin;
+    nry = x * -_sin + y * _cos;
+    nrx0 = x0 * _cos + y0 * _sin;
+    nry0 = x0 * -_sin + y0 * _cos;
+    
+    udx = (nrx - nrx0) / 2 / rx; /* ux - umx */
+    udy = (nry - nry0) / 2 / ry; /* uy - umy */
+    umx = (nrx + nrx0) / 2 / rx;
+    umy = (nry + nry0) / 2 / ry;
+
+    udx2 = udx * udx;
+    udy2 = udy * udy;
+    udl2 = udx2 + udy2;
+
+    if(udy != 0) {
+	udcx = sqrtf((1 - udl2) * udl2) / (udy + udx2 / udy);
+	udcy = - udcx * udx / udy;
+    } else {
+	udcx = 0;
+	udcy = -sqrtf((1 - udl2) * udl2) / udx;
+    }
+
+    reflect = 0;
+    if(large)
+	reflect ^= 1;
+    if(sweep != 1)
+	reflect ^= 1;
+    if(reflect) {
+	udcx = -udcx;
+	udcy = -udcy;
+    }
+
+    nrcx = rx * (udcx + umx);
+    nrcy = ry * (udcy + umy);
+
+    *cx = nrcx * _cos - nrcy * _sin;
+    *cy = nrcx * _sin + nrcy * _cos;
+
+    *xx = rx * _cos + *cx;
+    *xy = rx * _sin + *cy;
+
+    return OK;
+}
+
+
+#define TAKE_NUM(r) do {			\
+	SKIP_SPACE(p);				\
+	old = p;				\
+	SKIP_NUM(p);				\
+	if(p == old)				\
+	    return ERR;				\
+	r = atof(old);				\
+    } while(0);
+
+static int sh_path_arc_cmd_arg_fill(char cmd, char **cmds_p,
+				    const char **data_p,
+				    co_aix **args_p,
+				    int **fix_args_p) {
+    co_aix rx, ry;
+    co_aix x_rotate;
+    int large, sweep;
+    co_aix x, y, x0, y0, cx, cy, xx, xy;
+    co_aix *args = *args_p;
+    const char *old;
+    const char *p;
+    char *cmds;
+    int *fix_args;
+
+    p = *data_p;
+    cmds = *cmds_p;
+    fix_args = *fix_args_p;
+    while(*p) {
+	SKIP_SPACE(p);
+	old = p;
+	SKIP_NUM(p);
+	if(p == old)
+	    break;
+	TAKE_NUM(rx);
+
+	TAKE_NUM(ry);
+	TAKE_NUM(x_rotate);
+	TAKE_NUM(large);
+	TAKE_NUM(sweep);
+	TAKE_NUM(x);
+	TAKE_NUM(y)
+
+	x0 = *(args - 2);
+	y0 = *(args - 1);
+
+	if(islower(cmd)) {
+	    x += x0;
+	    y += y0;
+	}
+
+	calc_center_and_x_aix(x0, y0, x, y,
+			      rx, ry,
+			      x_rotate, large, sweep,
+			      &cx, &cy, &xx, &xy);
+
+	*(args++) = cx;
+	*(args++) = cy;
+	*(args++) = xx;
+	*(args++) = xy;
+	*(args++) = x;
+	*(args++) = y;
+
+	*cmds++ = toupper(cmd);
+	*fix_args++ = sweep;
+    }
+
+    *data_p = p;
+    *args_p = args;
+    *cmds_p = cmds;
+    *fix_args_p = fix_args;
+
+    return OK;
+}
+
+#define INNER(x1, y1, x2, y2) ((x1) * (x2) + (y1) * (y2))
+#define CROSS(x1, y1, x2, y2) ((x1) * (y2) - (y1) * (x2))
+
+void sh_path_arc_path(cairo_t *cr, const co_aix **args_p,
+		      const int **fix_args_p) {
+    co_aix cx, cy, x0, y0, x, y, xx, xy;
+    co_aix dx, dy, dx0, dy0, dxx, dxy;
+    co_aix xyratio;
+    co_aix rx;
+    co_aix rx2;
+    co_aix inner0, cross0;
+    co_aix circle_h0;
+    co_aix inner, cross;
+    co_aix angle, angle0;
+    co_aix rotate;
+    const co_aix *args = *args_p;
+    const int *fix_args = *fix_args_p;
+    int sweep;
+
+    x0 = *(args - 2);
+    y0 = *(args - 1);
+    cx = *args++;
+    cy = *args++;
+    xx = *args++;
+    xy = *args++;
+    x = *args++;
+    y = *args++;
+    sweep = *fix_args++;
+
+    dx = x - cx;
+    dy = y = cy;
+    dx0 = x0 - cx;
+    dy0 = y0 - cy;
+    dxx = xx - cx;
+    dxy = xy = cy;
+
+    rx2 = dxx * dxx + dxy * dxy;
+    rx = sqrtf(rx2);
+
+    /*! \note  Why we calculate these numbers there?
+     * If we compute it when filling arguments, sh_path_arc_cmd_arg_fill(),
+     * we can avoid to recompute it for every drawing.  But, transforming of
+     * coordinate can effect value of the numbers.
+     */
+    inner0 = INNER(dx0, dy0, dxx, dxy);
+    cross0 = CROSS(dx0, dy0, dxx, dxy);
+    circle_h0 = sqrtf(rx2 - inner0 * inner0 / rx2);
+    xyratio = cross0 / rx / circle_h0;
+    if(xyratio < 0)
+	xyratio = -xyratio;
+
+    angle0 = acos(inner / rx2);
+    if(cross0 < 0)
+	angle0 += PI;		/* 3rd, 4th Quadrant */
+
+    inner = INNER(dx, dy, dxx, dxy);
+    cross = CROSS(dx, dy, dxx, dxy);
+    angle = acos(inner / rx2);
+    if(cross < 0)
+	angle += PI;		/* 3rd, 4th Quadrant */
+
+    /* Make a path for arc */
+    rotate = acos(dxx / rx);
+    cairo_save(cr);
+    cairo_translate(cr, cx, cy);
+    cairo_rotate(cr, rotate);
+    cairo_scale(cr, 1.0, xyratio);
+    if(sweep)
+	cairo_arc(cr, 0, 0, rx, angle0, angle);
+    else
+	cairo_arc_negative(cr, 0, 0, rx, angle0, angle);
+    cairo_restore(cr);
+
+    *args_p = args;
+    *fix_args_p = fix_args;
+}
+
+/* ============================================================ */
 
 static void sh_path_free(shape_t *shape) {
     sh_path_t *path = (sh_path_t *)shape;
@@ -50,12 +305,13 @@
  *
  * \todo Notify programmers that syntax or value error of path data.
  */
-static int sh_path_cmd_arg_cnt(char *data, int *cmd_cntp, int *arg_cntp) {
+static int sh_path_cmd_arg_cnt(char *data, int *cmd_cntp, int *arg_cntp,
+			       int *fix_arg_cntp) {
     char *p, *old;
-    int cmd_cnt, arg_cnt;
+    int cmd_cnt, arg_cnt, fix_arg_cnt;
     int i;
 
-    cmd_cnt = arg_cnt = 0;
+    cmd_cnt = arg_cnt = fix_arg_cnt = 0;
     p = data;
     SKIP_SPACE(p);
     while(*p) {
@@ -203,6 +459,7 @@
 		}
 
 		arg_cnt += 6;
+		fix_arg_cnt++;
 
 		cmd_cnt++;
 	    }
@@ -220,173 +477,10 @@
 
     *cmd_cntp = cmd_cnt;
     *arg_cntp = arg_cnt;
-    return OK;
-}
-
-#include <math.h>
-/*! \brief Calculate center of the ellipse of an arc.
- *
- * - ux0 = x0 / rx
- * - uy0 = y0 / ry
- * - ux = x / rx
- * - uy = y / rx
- * ux0, uy0, ux, yu are got by transforming (x0, y0) and (x, y) into points
- * on the unit circle. The center of unit circle are (ucx, ucy):
- * - umx = (ux0 + ux) / 2
- * - umy = (uy0 + uy) / 2
- * - udcx = ucx - umx
- * - udcy = ucy - umy
- * - udx = ux - umx
- * - udy = uy - umy
- * - udcx * udx + udcy * udy = 0
- * - udcy = - udcx * udx / udy
- * - udcx ** 2 + udcy ** 2 + udx ** 2 + udy ** 2 = 1
- * - udcx ** 2 + (udcx * udx / udy) ** 2 = 1 - udx ** 2 - udy ** 2
- * - udcx ** 2 = (1 - udx ** 2 - udy ** 2) / (1 + (udx/udy) ** 2)
- *
- * - cx = rx * ucx
- * - cx = rx * (udcx + umx)
- * - cy = ry * ucy
- * - cy = ry * (udcy + umy)
- */
-static int calc_center_and_x_aix(co_aix x0, co_aix y0,
-				 co_aix x, co_aix y,
-				 co_aix rx, co_aix ry,
-				 co_aix x_rotate,
-				 int large, int sweep,
-				 co_aix *cx, co_aix *cy,
-				 co_aix *xx, co_aix *xy) {
-    co_aix nrx, nry, nrx0, nry0;
-    co_aix udx, udy, udx2, udy2;
-    co_aix umx, umy;
-    co_aix udcx, udcy;
-    co_aix nrcx, nrcy;
-    float _sin = sinf(x_rotate);
-    float _cos = cosf(x_rotate);
-    
-    nrx = x * _cos + y * _sin;
-    nry = x * -_sin + y * _cos;
-    nrx0 = x0 * _cos + y0 * _sin;
-    nry0 = x0 * -_sin + y0 * _cos;
-
-    udx = (nrx - nrx0) / 2 / rx;
-    udy = (nry - nry0) / 2 / ry;
-    umx = (nrx + nrx0) / 2 / rx;
-    umy = (nry + nry0) / 2 / ry;
-    udx2 = udx * udx;
-    udy2 = udy * udy;
-    udcx = sqrtf((1 - udx2 - udy2) / (1 + udx2 / udy2));
-    udcy = - udcx * udx / udy;
-    nrcx = rx * (udcx + umx);
-    nrcy = ry * (udcy + umy);
-
-    *cx = nrcx * _cos - nrcy * _sin;
-    *cy = nrcx * _sin + nrcy * _cos;
-
-    *xx = rx * _cos + *cx;
-    *xy = rx * _sin + *cy;
-
+    *fix_arg_cntp = fix_arg_cnt;
     return OK;
 }
 
-
-static int sh_path_arc_cmd_arg_fill(char cmd, char **cmds_p,
-				    const char **data_p, co_aix **args_p) {
-    co_aix rx, ry;
-    co_aix x_rotate;
-    int large, sweep;
-    co_aix x, y, x0, y0, cx, cy, xx, xy;
-    co_aix *args = *args_p;
-    const char *old;
-    const char *p;
-    char *cmds;
-
-    p = *data_p;
-    cmds = *cmds_p;
-    while(*p) {
-	SKIP_SPACE(p);
-	old = p;
-	SKIP_NUM(p);
-	if(p == old)
-	    break;
-	rx = atof(old);
-
-	SKIP_SPACE(p);
-	old = p;
-	SKIP_NUM(p);
-	if(p == old)
-	    return ERR;
-	ry = atof(old);
-
-	SKIP_SPACE(p);
-	old = p;
-	SKIP_NUM(p);
-	if(p == old)
-	    return ERR;
-	x_rotate = atof(old);
-
-	SKIP_SPACE(p);
-	old = p;
-	SKIP_NUM(p);
-	if(p == old)
-	    return ERR;
-	large = atoi(old);
-	
-	SKIP_SPACE(p);
-	old = p;
-	SKIP_NUM(p);
-	if(p == old)
-	    return ERR;
-	sweep = atoi(old);
-
-	SKIP_SPACE(p);
-	old = p;
-	SKIP_NUM(p);
-	if(p == old)
-	    return ERR;
-	x = atof(old);
-
-	SKIP_SPACE(p);
-	old = p;
-	SKIP_NUM(p);
-	if(p == old)
-	    return ERR;
-	y = atof(old);
-
-	x0 = *(args - 2);
-	y0 = *(args - 1);
-
-	if(islower(cmd)) {
-	    x += x0;
-	    y += y0;
-	}
-
-	calc_center_and_x_aix(x0, y0, x, y,
-			      rx, ry,
-			      x_rotate, large, sweep,
-			      &cx, &cy, &xx, &xy);
-
-	*(args++) = cx;
-	*(args++) = cy;
-	*(args++) = xx;
-	*(args++) = xy;
-	*(args++) = x;
-	*(args++) = y;
-
-	*cmds++ = toupper(cmd);
-    }
-
-    *data_p = p;
-    *args_p = args;
-    *cmds_p = cmds;
-
-    return OK;
-}
-
-void sh_path_arc_path(cairo_t *cr, const co_aix **args) {
-    
-}
-
 #define TO_ABSX islower(cmd)? x + atof(old): atof(old)
 #define TO_ABSY islower(cmd)? y + atof(old): atof(old)
 
@@ -395,11 +489,14 @@
     char *cmds;
     char cmd;
     co_aix *args;
+    int *fix_args;
     co_aix x, y;
     int r;
 
     cmds = path->user_data;
     args = (co_aix *)(cmds + path->cmd_len);
+    fix_args = (int *)(cmds + path->cmd_len +
+		       path->arg_len * sizeof(co_aix));
     p = data;
     SKIP_SPACE(p);
     while(*p) {
@@ -539,7 +636,9 @@
 
 	case 'A':
 	case 'a':
-	    r = sh_path_arc_cmd_arg_fill(cmd, &cmds, (const char **)&p, &args);
+	    r = sh_path_arc_cmd_arg_fill(cmd, &cmds,
+					 (const char **)&p, &args,
+					 &fix_args);
 	    if(r != OK)
 		return ERR;
 	    break;
@@ -561,10 +660,11 @@
  */
 shape_t *sh_path_new(char *data) {
     sh_path_t *path;
-    int cmd_cnt, arg_cnt;
+    int cmd_cnt, arg_cnt, fix_arg_cnt;
+    int msz;
     int r;
 
-    r = sh_path_cmd_arg_cnt(data, &cmd_cnt, &arg_cnt);
+    r = sh_path_cmd_arg_cnt(data, &cmd_cnt, &arg_cnt, &fix_arg_cnt);
     if(r == ERR)
 	return NULL;
 
@@ -580,12 +680,14 @@
     path->shape.sh_type = SHT_PATH;
     path->cmd_len = cmd_cnt;
     path->arg_len = arg_cnt;
-    path->user_data = (char *)malloc(cmd_cnt + sizeof(co_aix) * arg_cnt);
+    path->fix_arg_len = fix_arg_cnt;
+    msz = cmd_cnt + sizeof(co_aix) * arg_cnt + sizeof(int) * fix_arg_cnt;
+    path->user_data = (char *)malloc(msz);
     if(path->user_data == NULL) {
 	free(path);
 	return NULL;
     }
-    path->dev_data = (char *)malloc(cmd_cnt + sizeof(co_aix) * arg_cnt);
+    path->dev_data = (char *)malloc(msz);
     if(path->dev_data == NULL) {
 	free(path->dev_data);
 	free(path);
@@ -650,6 +752,7 @@
     int cmd_len;
     char *cmds, cmd;
     const co_aix *args;
+    const int *fix_args;
     co_aix x, y, x1, y1, x2, y2;
     int i;
 
@@ -659,6 +762,7 @@
     cmd_len = path->cmd_len;
     cmds = path->dev_data;
     args = (co_aix *)(cmds + cmd_len);
+    fix_args = (int *)(cmds + cmd_len + path->arg_len * sizeof(co_aix));
     x = y = x1 = y1 = x2 = y2 = 0;
     for(i = 0; i < cmd_len; i++) {
 	/* All path commands and arguments are transformed
@@ -713,7 +817,7 @@
 	    cairo_curve_to(cr, x1, y1, x2, y2, x, y);
 	    break;
 	case 'A':
-	    sh_path_arc_path(cr, &args);
+	    sh_path_arc_path(cr, &args, &fix_args);
 	    break;
 	case 'Z':
 	    cairo_close_path(cr);