libgeda
|
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 }