libgeda

s_path.c

Go to the documentation of this file.
00001 /* gEDA - GPL Electronic Design Automation
00002  * libgeda - gEDA's library
00003  * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
00004  *
00005  * Code originally from librsvg 2.22.2 (LGPL) Copyright (C) 2000 Eazel, Inc.
00006  *
00007  *   Author: Raph Levien <raph@artofcode.com>
00008  *     rsvg-path.c:       Parse SVG path element data into bezier path.
00009  *     rsvg-bpath-util.c: Data structure and convenience functions for
00010  *                        creating bezier paths.
00011  *
00012  *  Adapted for gEDA by Peter Clifton <pcjc2@cam.ac.uk>
00013  *
00014  *  THIS FILE IS LGPL LICENSED, gEDA AS A WHOLE IS GPL LICENSED
00015  *
00016  * This program is free software; you can redistribute it and/or
00017  *  modify it under the terms of the GNU Library General Public License as
00018  *  published by the Free Software Foundation; either version 2 of the
00019  *  License, or (at your option) any later version.
00020  *
00021  *  This program is distributed in the hope that it will be useful,
00022  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00023  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00024  *  Library General Public License for more details.
00025  *
00026  *  You should have received a copy of the GNU Library General Public
00027  *  License along with this program; if not, write to the
00028  *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00029  *  Boston, MA 02110-1301 USA
00030  *
00031  */
00032 
00033 #include "config.h"
00034 
00035 #include <stdio.h>
00036 #include <math.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 
00040 #include <glib.h>
00041 
00042 #include "libgeda_priv.h"
00043 
00044 #define NUM_BEZIER_SEGMENTS 100
00045 
00046 
00047 PATH *s_path_new (void)
00048 {
00049   PATH *path;
00050 
00051   path = g_new (PATH, 1);
00052   path->num_sections = 0;
00053   path->num_sections_max = 16;
00054   path->sections = g_new (PATH_SECTION, path->num_sections_max);
00055 
00056   return path;
00057 }
00058 
00059 
00060 PATH *s_path_new_from (PATH_SECTION *sections)
00061 {
00062   PATH *path;
00063   int i;
00064 
00065   g_return_val_if_fail (sections != NULL, NULL);
00066 
00067   for (i = 0; sections[i].code != PATH_END; i++);
00068   if (i <= 0)
00069     return s_path_new ();
00070 
00071   path = g_new (PATH, 1);
00072 
00073   path->num_sections = i;
00074   path->num_sections_max = i;
00075   path->sections = g_new (PATH_SECTION, i);
00076 
00077   memcpy (path->sections, sections, i * sizeof (PATH_SECTION));
00078   return path;
00079 }
00080 
00081 
00082 void s_path_free (PATH * path)
00083 {
00084   g_return_if_fail (path != NULL);
00085 
00086   g_free (path->sections);
00087   g_free (path);
00088 }
00089 
00090 
00091 void s_path_moveto (PATH *path, double x, double y)
00092 {
00093   PATH_SECTION *sections;
00094   int num_sections;
00095 
00096   g_return_if_fail (path != NULL);
00097 
00098   /* if the last command was a moveto then change that last moveto instead of
00099      creating a new one */
00100   sections = path->sections;
00101   num_sections = path->num_sections;
00102 
00103   if (num_sections > 0)
00104     if (sections[num_sections - 1].code == PATH_MOVETO_OPEN) {
00105       sections[num_sections - 1].x3 = x;
00106       sections[num_sections - 1].y3 = y;
00107       return;
00108     }
00109 
00110   num_sections = path->num_sections++;
00111 
00112   if (num_sections == path->num_sections_max)
00113     path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
00114   sections = path->sections;
00115   sections[num_sections].code = PATH_MOVETO_OPEN;
00116   sections[num_sections].x3 = x;
00117   sections[num_sections].y3 = y;
00118 }
00119 
00120 
00121 void s_path_lineto (PATH *path, double x, double y)
00122 {
00123   PATH_SECTION *sections;
00124   int num_sections;
00125 
00126   g_return_if_fail (path != NULL);
00127 
00128   num_sections = path->num_sections++;
00129 
00130   if (num_sections == path->num_sections_max)
00131     path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
00132   sections = path->sections;
00133   sections[num_sections].code = PATH_LINETO;
00134   sections[num_sections].x3 = x;
00135   sections[num_sections].y3 = y;
00136 }
00137 
00138 
00139 void s_path_curveto (PATH *path, double x1, double y1,
00140                      double x2, double y2, double x3, double y3)
00141 {
00142   PATH_SECTION *sections;
00143   int num_sections;
00144 
00145   g_return_if_fail (path != NULL);
00146 
00147   num_sections = path->num_sections++;
00148 
00149   if (num_sections == path->num_sections_max)
00150     path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
00151   sections = path->sections;
00152   sections[num_sections].code = PATH_CURVETO;
00153   sections[num_sections].x1 = x1;
00154   sections[num_sections].y1 = y1;
00155   sections[num_sections].x2 = x2;
00156   sections[num_sections].y2 = y2;
00157   sections[num_sections].x3 = x3;
00158   sections[num_sections].y3 = y3;
00159 }
00160 
00161 
00162 void s_path_art_finish (PATH * path)
00163 {
00164   int num_sections;
00165 
00166   g_return_if_fail (path != NULL);
00167 
00168   num_sections = path->num_sections++;
00169 
00170   if (num_sections == path->num_sections_max)
00171     path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
00172   path->sections[num_sections].code = PATH_END;
00173 }
00174 
00175 
00176 /* This module parses an SVG style path element into a PATH.
00177 
00178   At present, there is no support for <marker> or any other contextual
00179   information from the SVG file. The API will need to change rather
00180   significantly to support these.
00181 
00182   Reference: SVG working draft 3 March 2000, section 8.
00183 */
00184 
00185 typedef struct _RSVGParsePathCtx RSVGParsePathCtx;
00186 
00187 struct _RSVGParsePathCtx {
00188   PATH *path;
00189   double cpx, cpy;    /* current point */
00190   double rpx, rpy;    /* reflection point (for 's' and 't' commands) */
00191   double mpx, mpy;    /* Last moved to point (for path closures) */
00192   char cmd;           /* current command (lowercase) */
00193   int param;          /* parameter number */
00194   gboolean rel;       /* true if relative coords */
00195   double params[7];   /* parameters that have been parsed */
00196 };
00197 
00198 
00199 static void s_path_arc_segment (RSVGParsePathCtx * ctx,
00200                                 double xc, double yc, double th0, double th1,
00201                                 double rx, double ry, double x_axis_rotation)
00202 {
00203   double sin_th, cos_th;
00204   double a00, a01, a10, a11;
00205   double x1, y1, x2, y2, x3, y3;
00206   double t;
00207   double th_half;
00208 
00209   sin_th = sin (x_axis_rotation * (M_PI / 180.0));
00210   cos_th = cos (x_axis_rotation * (M_PI / 180.0));
00211   /* inverse transform compared with s_path_arc */
00212   a00 = cos_th * rx;
00213   a01 = -sin_th * ry;
00214   a10 = sin_th * rx;
00215   a11 = cos_th * ry;
00216 
00217   th_half = 0.5 * (th1 - th0);
00218   t = (8.0 / 3.0) * sin (th_half * 0.5) * sin (th_half * 0.5) / sin (th_half);
00219   x1 = xc + cos (th0) - t * sin (th0);
00220   y1 = yc + sin (th0) + t * cos (th0);
00221   x3 = xc + cos (th1);
00222   y3 = yc + sin (th1);
00223   x2 = x3 + t * sin (th1);
00224   y2 = y3 - t * cos (th1);
00225   s_path_curveto (ctx->path,
00226               a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
00227               a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
00228               a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
00229 }
00230 
00231 
00232 /*
00233  * s_path_arc: Add an arc to the path context.
00234  * @ctx: Path context.
00235  * @rx: Radius in x direction (before rotation).
00236  * @ry: Radius in y direction (before rotation).
00237  * @x_axis_rotation: Rotation angle for axes.
00238  * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
00239  * @sweep: 0 for "negative angle", 1 for "positive angle".
00240  * @x: New x coordinate.
00241  * @y: New y coordinate.
00242  *
00243  */
00244 static void s_path_arc (RSVGParsePathCtx * ctx,
00245                         double rx, double ry, double x_axis_rotation,
00246                         int large_arc_flag, int sweep_flag, double x, double y)
00247 {
00248   double sin_th, cos_th;
00249   double a00, a01, a10, a11;
00250   double x0, y0, x1, y1, xc, yc;
00251   double d, sfactor, sfactor_sq;
00252   double th0, th1, th_arc;
00253   int i, n_segs;
00254 
00255   /* Check that neither radius is zero, since its isn't either
00256      geometrically or mathematically meaningful and will
00257      cause divide by zero and subsequent NaNs.  We should
00258      really do some ranged check ie -0.001 < x < 000.1 rather
00259      can just a straight check again zero.
00260    */
00261   if ((rx == 0.0) || (ry == 0.0))
00262     return;
00263 
00264   sin_th = sin (x_axis_rotation * (M_PI / 180.0));
00265   cos_th = cos (x_axis_rotation * (M_PI / 180.0));
00266   a00 = cos_th / rx;
00267   a01 = sin_th / rx;
00268   a10 = -sin_th / ry;
00269   a11 = cos_th / ry;
00270   x0 = a00 * ctx->cpx + a01 * ctx->cpy;
00271   y0 = a10 * ctx->cpx + a11 * ctx->cpy;
00272   x1 = a00 * x + a01 * y;
00273   y1 = a10 * x + a11 * y;
00274   /* (x0, y0) is current point in transformed coordinate space.
00275      (x1, y1) is new point in transformed coordinate space.
00276 
00277      The arc fits a unit-radius circle in this space.
00278    */
00279   d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
00280   sfactor_sq = 1.0 / d - 0.25;
00281   if (sfactor_sq < 0)
00282     sfactor_sq = 0;
00283   sfactor = sqrt (sfactor_sq);
00284   if (sweep_flag == large_arc_flag)
00285     sfactor = -sfactor;
00286   xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
00287   yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
00288   /* (xc, yc) is center of the circle. */
00289 
00290   th0 = atan2 (y0 - yc, x0 - xc);
00291   th1 = atan2 (y1 - yc, x1 - xc);
00292 
00293   th_arc = th1 - th0;
00294   if (th_arc < 0 && sweep_flag)
00295     th_arc += 2 * M_PI;
00296   else if (th_arc > 0 && !sweep_flag)
00297     th_arc -= 2 * M_PI;
00298 
00299   n_segs = ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
00300 
00301   for (i = 0; i < n_segs; i++)
00302     s_path_arc_segment (ctx, xc, yc,
00303                  th0 + i * th_arc / n_segs,
00304                  th0 + (i + 1) * th_arc / n_segs, rx, ry, x_axis_rotation);
00305 
00306   ctx->cpx = x;
00307   ctx->cpy = y;
00308 }
00309 
00310 
00311 /* supply defaults for missing parameters, assuming relative coordinates
00312    are to be interpreted as x,y */
00313 static void s_path_parse_default_xy (RSVGParsePathCtx * ctx, int n_params)
00314 {
00315   int i;
00316 
00317   if (ctx->rel) {
00318     for (i = ctx->param; i < n_params; i++) {
00319       if (i > 2)
00320         ctx->params[i] = ctx->params[i - 2];
00321       else if (i == 1)
00322         ctx->params[i] = ctx->cpy;
00323       else if (i == 0)
00324         /* we shouldn't get here (usually ctx->param > 0 as
00325            precondition) */
00326         ctx->params[i] = ctx->cpx;
00327     }
00328   } else {
00329     for (i = ctx->param; i < n_params; i++)
00330       ctx->params[i] = 0.0;
00331   }
00332 }
00333 
00334 
00335 static void s_path_parse_do_cmd (RSVGParsePathCtx * ctx, gboolean final)
00336 {
00337   double x1, y1, x2, y2, x3, y3;
00338 
00339   switch (ctx->cmd) {
00340   case 'm':
00341     /* moveto */
00342     if (ctx->param == 2 || final) {
00343       s_path_parse_default_xy (ctx, 2);
00344       s_path_moveto (ctx->path, ctx->params[0], ctx->params[1]);
00345       ctx->mpx = ctx->cpx = ctx->rpx = ctx->params[0];
00346       ctx->mpy = ctx->cpy = ctx->rpy = ctx->params[1];
00347       ctx->param = 0;
00348       ctx->cmd = 'l'; /* implicit linetos after a moveto */
00349     }
00350     break;
00351   case 'l':
00352     /* lineto */
00353     if (ctx->param == 2 || final) {
00354       s_path_parse_default_xy (ctx, 2);
00355       s_path_lineto (ctx->path, ctx->params[0], ctx->params[1]);
00356       ctx->cpx = ctx->rpx = ctx->params[0];
00357       ctx->cpy = ctx->rpy = ctx->params[1];
00358       ctx->param = 0;
00359     }
00360     break;
00361   case 'c':
00362     /* curveto */
00363     if (ctx->param == 6 || final) {
00364       s_path_parse_default_xy (ctx, 6);
00365       x1 = ctx->params[0];
00366       y1 = ctx->params[1];
00367       x2 = ctx->params[2];
00368       y2 = ctx->params[3];
00369       x3 = ctx->params[4];
00370       y3 = ctx->params[5];
00371       s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
00372       ctx->rpx = x2;
00373       ctx->rpy = y2;
00374       ctx->cpx = x3;
00375       ctx->cpy = y3;
00376       ctx->param = 0;
00377     }
00378     break;
00379   case 's':
00380     /* smooth curveto */
00381     if (ctx->param == 4 || final) {
00382       s_path_parse_default_xy (ctx, 4);
00383       x1 = 2 * ctx->cpx - ctx->rpx;
00384       y1 = 2 * ctx->cpy - ctx->rpy;
00385       x2 = ctx->params[0];
00386       y2 = ctx->params[1];
00387       x3 = ctx->params[2];
00388       y3 = ctx->params[3];
00389       s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
00390       ctx->rpx = x2;
00391       ctx->rpy = y2;
00392       ctx->cpx = x3;
00393       ctx->cpy = y3;
00394       ctx->param = 0;
00395     }
00396     break;
00397   case 'h':
00398     /* horizontal lineto */
00399     if (ctx->param == 1) {
00400       s_path_lineto (ctx->path, ctx->params[0], ctx->cpy);
00401       ctx->cpx = ctx->rpx = ctx->params[0];
00402       ctx->param = 0;
00403     }
00404     break;
00405   case 'v':
00406     /* vertical lineto */
00407     if (ctx->param == 1) {
00408       s_path_lineto (ctx->path, ctx->cpx, ctx->params[0]);
00409       ctx->cpy = ctx->rpy = ctx->params[0];
00410       ctx->param = 0;
00411     }
00412     break;
00413   case 'q':
00414     /* quadratic bezier curveto */
00415 
00416     /* non-normative reference:
00417        http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
00418      */
00419     if (ctx->param == 4 || final) {
00420       s_path_parse_default_xy (ctx, 4);
00421       /* raise quadratic bezier to cubic */
00422       x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
00423       y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
00424       x3 = ctx->params[2];
00425       y3 = ctx->params[3];
00426       x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
00427       y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
00428       s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
00429       ctx->rpx = ctx->params[0];
00430       ctx->rpy = ctx->params[1];
00431       ctx->cpx = x3;
00432       ctx->cpy = y3;
00433       ctx->param = 0;
00434     }
00435     break;
00436   case 't':
00437     /* Truetype quadratic bezier curveto */
00438     if (ctx->param == 2 || final) {
00439       double xc, yc;    /* quadratic control point */
00440 
00441       xc = 2 * ctx->cpx - ctx->rpx;
00442       yc = 2 * ctx->cpy - ctx->rpy;
00443       /* generate a quadratic bezier with control point = xc, yc */
00444       x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
00445       y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
00446       x3 = ctx->params[0];
00447       y3 = ctx->params[1];
00448       x2 = (x3 + 2 * xc) * (1.0 / 3.0);
00449       y2 = (y3 + 2 * yc) * (1.0 / 3.0);
00450       s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
00451       ctx->rpx = xc;
00452       ctx->rpy = yc;
00453       ctx->cpx = x3;
00454       ctx->cpy = y3;
00455       ctx->param = 0;
00456     } else if (final) {
00457       if (ctx->param > 2) {
00458         s_path_parse_default_xy (ctx, 4);
00459         /* raise quadratic bezier to cubic */
00460         x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
00461         y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
00462         x3 = ctx->params[2];
00463         y3 = ctx->params[3];
00464         x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
00465         y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
00466         s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
00467         ctx->rpx = ctx->params[0];
00468         ctx->rpy = ctx->params[1];
00469         ctx->cpx = x3;
00470         ctx->cpy = y3;
00471       } else {
00472         s_path_parse_default_xy (ctx, 2);
00473         s_path_lineto (ctx->path, ctx->params[0], ctx->params[1]);
00474         ctx->cpx = ctx->rpx = ctx->params[0];
00475         ctx->cpy = ctx->rpy = ctx->params[1];
00476       }
00477       ctx->param = 0;
00478     }
00479     break;
00480   case 'a':
00481     if (ctx->param == 7 || final) {
00482       s_path_arc (ctx,
00483                ctx->params[0], ctx->params[1], ctx->params[2],
00484                ctx->params[3], ctx->params[4], ctx->params[5], ctx->params[6]);
00485       ctx->param = 0;
00486     }
00487     break;
00488   default:
00489     ctx->param = 0;
00490   }
00491 }
00492 
00493 
00494 static void s_path_parse_data (RSVGParsePathCtx * ctx, const char *data)
00495 {
00496   int i = 0;
00497   double val = 0;
00498   char c = 0;
00499   gboolean in_num = FALSE;
00500   gboolean in_frac = FALSE;
00501   gboolean in_exp = FALSE;
00502   gboolean exp_wait_sign = FALSE;
00503   int sign = 0;
00504   int exp = 0;
00505   int exp_sign = 0;
00506   double frac = 0.0;
00507 
00508   in_num = FALSE;
00509   for (i = 0;; i++) {
00510     c = data[i];
00511     if (c >= '0' && c <= '9') {
00512       /* digit */
00513       if (in_num) {
00514         if (in_exp) {
00515           exp = (exp * 10) + c - '0';
00516           exp_wait_sign = FALSE;
00517         } else if (in_frac)
00518           val += (frac *= 0.1) * (c - '0');
00519         else
00520           val = (val * 10) + c - '0';
00521       } else {
00522         in_num = TRUE;
00523         in_frac = FALSE;
00524         in_exp = FALSE;
00525         exp = 0;
00526         exp_sign = 1;
00527         exp_wait_sign = FALSE;
00528         val = c - '0';
00529         sign = 1;
00530       }
00531     } else if (c == '.') {
00532       if (!in_num) {
00533         in_num = TRUE;
00534         val = 0;
00535       }
00536       in_frac = TRUE;
00537       frac = 1;
00538     } else if ((c == 'E' || c == 'e') && in_num) {
00539       in_exp = TRUE;
00540       exp_wait_sign = TRUE;
00541       exp = 0;
00542       exp_sign = 1;
00543     } else if ((c == '+' || c == '-') && in_exp) {
00544       exp_sign = c == '+' ? 1 : -1;
00545     } else if (in_num) {
00546       /* end of number */
00547 
00548       val *= sign * pow (10, exp_sign * exp);
00549       if (ctx->rel) {
00550         /* Handle relative coordinates. This switch statement attempts
00551            to determine _what_ the coords are relative to. This is
00552            underspecified in the 12 Apr working draft. */
00553         switch (ctx->cmd) {
00554         case 'l':
00555         case 'm':
00556         case 'c':
00557         case 's':
00558         case 'q':
00559         case 't':
00560 #ifndef RSVGV_RELATIVE
00561           /* rule: even-numbered params are x-relative, odd-numbered
00562              are y-relative */
00563           if ((ctx->param & 1) == 0)
00564             val += ctx->cpx;
00565           else if ((ctx->param & 1) == 1)
00566             val += ctx->cpy;
00567           break;
00568 #else
00569           /* rule: even-numbered params are x-relative, odd-numbered
00570              are y-relative */
00571           if (ctx->param == 0 || (ctx->param % 2 == 0))
00572             val += ctx->cpx;
00573           else
00574             val += ctx->cpy;
00575           break;
00576 #endif
00577         case 'a':
00578           /* rule: sixth and seventh are x and y, rest are not
00579              relative */
00580           if (ctx->param == 5)
00581             val += ctx->cpx;
00582           else if (ctx->param == 6)
00583             val += ctx->cpy;
00584           break;
00585         case 'h':
00586           /* rule: x-relative */
00587           val += ctx->cpx;
00588           break;
00589         case 'v':
00590           /* rule: y-relative */
00591           val += ctx->cpy;
00592           break;
00593         }
00594       }
00595       ctx->params[ctx->param++] = val;
00596       s_path_parse_do_cmd (ctx, FALSE);
00597 
00598       in_num = FALSE;
00599     }
00600 
00601     if (c == '\0')
00602       break;
00603     else if ((c == '+' || c == '-') && !exp_wait_sign) {
00604       sign = c == '+' ? 1 : -1;
00605       val = 0;
00606       in_num = TRUE;
00607       in_frac = FALSE;
00608       in_exp = FALSE;
00609       exp = 0;
00610       exp_sign = 1;
00611       exp_wait_sign = FALSE;
00612     } else if (c == 'z' || c == 'Z') {
00613       if (ctx->param)
00614         s_path_parse_do_cmd (ctx, TRUE);
00615       /* s_path_closepath (ctx->path); */
00616       /* s_path_lineto (ctx->path, ctx->mpx, ctx->mpy); */
00617       s_path_art_finish (ctx->path);
00618 
00619       ctx->cpx = ctx->rpx = ctx->path->sections[ctx->path->num_sections - 1].x3;
00620       ctx->cpy = ctx->rpy = ctx->path->sections[ctx->path->num_sections - 1].y3;
00621     } else if (c >= 'A' && c <= 'Z' && c != 'E') {
00622       if (ctx->param)
00623         s_path_parse_do_cmd (ctx, TRUE);
00624       ctx->cmd = c + 'a' - 'A';
00625       ctx->rel = FALSE;
00626     } else if (c >= 'a' && c <= 'z' && c != 'e') {
00627       if (ctx->param)
00628         s_path_parse_do_cmd (ctx, TRUE);
00629       ctx->cmd = c;
00630       ctx->rel = TRUE;
00631     }
00632     /* else c _should_ be whitespace or , */
00633   }
00634 }
00635 
00636 
00637 PATH *s_path_parse (const char *path_str)
00638 {
00639   RSVGParsePathCtx ctx;
00640 
00641   ctx.path = s_path_new ();
00642   ctx.cpx = 0.0;
00643   ctx.cpy = 0.0;
00644   ctx.mpx = 0.0;
00645   ctx.mpy = 0.0;
00646   ctx.cmd = 0;
00647   ctx.param = 0;
00648 
00649   s_path_parse_data (&ctx, path_str);
00650 
00651   if (ctx.param)
00652     s_path_parse_do_cmd (&ctx, TRUE);
00653 
00654   return ctx.path;
00655 }
00656 
00657 
00658 char *s_path_string_from_path (const PATH *path)
00659 {
00660   PATH_SECTION *section;
00661   GString *path_string;
00662   int i;
00663 
00664   path_string = g_string_new ("");
00665 
00666   for (i = 0; i < path->num_sections; i++) {
00667     section = &path->sections[i];
00668 
00669     if (i > 0)
00670       g_string_append_c (path_string, '\n');
00671 
00672     switch (section->code) {
00673       case PATH_MOVETO:
00674         g_string_append_printf (path_string, "M %i,%i",
00675                                 section->x3, section->y3);
00676         break;
00677       case PATH_MOVETO_OPEN:
00678         g_string_append_printf (path_string, "M %i,%i",
00679                                 section->x3, section->y3);
00680         break;
00681       case PATH_CURVETO:
00682         g_string_append_printf (path_string, "C %i,%i %i,%i %i,%i",
00683                                 section->x1, section->y1,
00684                                 section->x2, section->y2,
00685                                 section->x3, section->y3);
00686         break;
00687       case PATH_LINETO:
00688         g_string_append_printf (path_string, "L %i,%i",
00689                                 section->x3, section->y3);
00690         break;
00691       case PATH_END:
00692         g_string_append_printf (path_string, "z");
00693         break;
00694     }
00695   }
00696 
00697   return g_string_free (path_string, FALSE);
00698 }
00699 
00708 int s_path_to_polygon (PATH *path, GArray *points)
00709 {
00710   int closed = FALSE;
00711   int i;
00712   sPOINT point = { 0, 0 };
00713 
00714   if (points->len > 0) {
00715     g_array_remove_range (points, 0, points->len - 1);
00716   }
00717 
00718   for (i = 0; i < path->num_sections; i++) {
00719     BEZIER bezier;
00720     PATH_SECTION *section = &path->sections[i];
00721 
00722     switch (section->code) {
00723       case PATH_CURVETO:
00724         bezier.x[0] = point.x;
00725         bezier.y[0] = point.y;
00726         bezier.x[1] = section->x1;
00727         bezier.y[1] = section->y1;
00728         bezier.x[2] = section->x2;
00729         bezier.y[2] = section->y2;
00730         point.x = bezier.x[3] = section->x3;
00731         point.y = bezier.y[3] = section->y3;
00732         m_polygon_append_bezier (points, &bezier, NUM_BEZIER_SEGMENTS);
00733         break;
00734 
00735       case PATH_MOVETO_OPEN:
00736         /* Unsupported, just fall through and draw a line */
00737         /* Fall through */
00738 
00739       case PATH_MOVETO:
00740       case PATH_LINETO:
00741         point.x = section->x3;
00742         point.y = section->y3;
00743         m_polygon_append_point (points, point.x, point.y);
00744         break;
00745 
00746       case PATH_END:
00747         closed = TRUE;
00748         break;
00749     }
00750   }
00751 
00752   return closed;
00753 }
00754 
00755 
00768 double s_path_shortest_distance (PATH *path, int x, int y, int solid)
00769 {
00770   double shortest_distance = G_MAXDOUBLE;
00771   int closed;
00772   GArray *points;
00773 
00774   points = g_array_new (FALSE, FALSE, sizeof (sPOINT));
00775 
00776   closed = s_path_to_polygon (path, points);
00777 
00778   if (!solid) {
00779     shortest_distance = m_polygon_shortest_distance (points, x, y, closed);
00780 
00781   } else if (m_polygon_interior_point (points, x, y)) {
00782     shortest_distance = 0;
00783 
00784   } else {
00785     shortest_distance = m_polygon_shortest_distance (points, x, y, TRUE);
00786   }
00787 
00788   g_array_free (points, TRUE);
00789 
00790   return shortest_distance;
00791 }
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines