Updated copyright text/header in most source files.
[geda-gaf/peter-b.git] / libgeda / src / s_path.c
blobc2df0c5390db5358c23d38bbad0b5956b5c576fa
1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
5 * Code originally from librsvg 2.22.2 (LGPL) Copyright (C) 2000 Eazel, Inc.
7 * Author: Raph Levien <raph@artofcode.com>
8 * rsvg-path.c: Parse SVG path element data into bezier path.
9 * rsvg-bpath-util.c: Data structure and convenience functions for
10 * creating bezier paths.
12 * Adapted for gEDA by Peter Clifton <pcjc2@cam.ac.uk>
14 * THIS FILE IS LGPL LICENSED, gEDA AS A WHOLE IS GPL LICENSED
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Library General Public License as
18 * published by the Free Software Foundation; either version 2 of the
19 * License, or (at your option) any later version.
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Library General Public License for more details.
26 * You should have received a copy of the GNU Library General Public
27 * License along with this program; if not, write to the
28 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
29 * Boston, MA 02111-1307, USA.
33 #include "config.h"
35 #include <stdio.h>
36 #include <math.h>
37 #include <stdlib.h>
38 #include <string.h>
40 #include <glib/gmem.h>
41 #include <glib/gmessages.h>
42 #include <glib/gtypes.h>
44 #include "libgeda_priv.h"
46 #define NUM_BEZIER_SEGMENTS 100
49 PATH *s_path_new (void)
51 PATH *path;
53 path = g_new (PATH, 1);
54 path->num_sections = 0;
55 path->num_sections_max = 16;
56 path->sections = g_new (PATH_SECTION, path->num_sections_max);
58 return path;
62 PATH *s_path_new_from (PATH_SECTION *sections)
64 PATH *path;
65 int i;
67 g_return_val_if_fail (sections != NULL, NULL);
69 for (i = 0; sections[i].code != PATH_END; i++);
70 if (i <= 0)
71 return s_path_new ();
73 path = g_new (PATH, 1);
75 path->num_sections = i;
76 path->num_sections_max = i;
77 path->sections = g_new (PATH_SECTION, i);
79 memcpy (path->sections, sections, i * sizeof (PATH_SECTION));
80 return path;
84 void s_path_free (PATH * path)
86 g_return_if_fail (path != NULL);
88 g_free (path->sections);
89 g_free (path);
93 void s_path_moveto (PATH *path, double x, double y)
95 PATH_SECTION *sections;
96 int num_sections;
98 g_return_if_fail (path != NULL);
100 /* if the last command was a moveto then change that last moveto instead of
101 creating a new one */
102 sections = path->sections;
103 num_sections = path->num_sections;
105 if (num_sections > 0)
106 if (sections[num_sections - 1].code == PATH_MOVETO_OPEN) {
107 sections[num_sections - 1].x3 = x;
108 sections[num_sections - 1].y3 = y;
109 return;
112 num_sections = path->num_sections++;
114 if (num_sections == path->num_sections_max)
115 path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
116 sections = path->sections;
117 sections[num_sections].code = PATH_MOVETO_OPEN;
118 sections[num_sections].x3 = x;
119 sections[num_sections].y3 = y;
123 void s_path_lineto (PATH *path, double x, double y)
125 PATH_SECTION *sections;
126 int num_sections;
128 g_return_if_fail (path != NULL);
130 num_sections = path->num_sections++;
132 if (num_sections == path->num_sections_max)
133 path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
134 sections = path->sections;
135 sections[num_sections].code = PATH_LINETO;
136 sections[num_sections].x3 = x;
137 sections[num_sections].y3 = y;
141 void s_path_curveto (PATH *path, double x1, double y1,
142 double x2, double y2, double x3, double y3)
144 PATH_SECTION *sections;
145 int num_sections;
147 g_return_if_fail (path != NULL);
149 num_sections = path->num_sections++;
151 if (num_sections == path->num_sections_max)
152 path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
153 sections = path->sections;
154 sections[num_sections].code = PATH_CURVETO;
155 sections[num_sections].x1 = x1;
156 sections[num_sections].y1 = y1;
157 sections[num_sections].x2 = x2;
158 sections[num_sections].y2 = y2;
159 sections[num_sections].x3 = x3;
160 sections[num_sections].y3 = y3;
164 void s_path_art_finish (PATH * path)
166 int num_sections;
168 g_return_if_fail (path != NULL);
170 num_sections = path->num_sections++;
172 if (num_sections == path->num_sections_max)
173 path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
174 path->sections[num_sections].code = PATH_END;
178 /* This module parses an SVG style path element into a PATH.
180 At present, there is no support for <marker> or any other contextual
181 information from the SVG file. The API will need to change rather
182 significantly to support these.
184 Reference: SVG working draft 3 March 2000, section 8.
187 typedef struct _RSVGParsePathCtx RSVGParsePathCtx;
189 struct _RSVGParsePathCtx {
190 PATH *path;
191 double cpx, cpy; /* current point */
192 double rpx, rpy; /* reflection point (for 's' and 't' commands) */
193 double mpx, mpy; /* Last moved to point (for path closures) */
194 char cmd; /* current command (lowercase) */
195 int param; /* parameter number */
196 gboolean rel; /* true if relative coords */
197 double params[7]; /* parameters that have been parsed */
201 static void s_path_arc_segment (RSVGParsePathCtx * ctx,
202 double xc, double yc, double th0, double th1,
203 double rx, double ry, double x_axis_rotation)
205 double sin_th, cos_th;
206 double a00, a01, a10, a11;
207 double x1, y1, x2, y2, x3, y3;
208 double t;
209 double th_half;
211 sin_th = sin (x_axis_rotation * (M_PI / 180.0));
212 cos_th = cos (x_axis_rotation * (M_PI / 180.0));
213 /* inverse transform compared with s_path_arc */
214 a00 = cos_th * rx;
215 a01 = -sin_th * ry;
216 a10 = sin_th * rx;
217 a11 = cos_th * ry;
219 th_half = 0.5 * (th1 - th0);
220 t = (8.0 / 3.0) * sin (th_half * 0.5) * sin (th_half * 0.5) / sin (th_half);
221 x1 = xc + cos (th0) - t * sin (th0);
222 y1 = yc + sin (th0) + t * cos (th0);
223 x3 = xc + cos (th1);
224 y3 = yc + sin (th1);
225 x2 = x3 + t * sin (th1);
226 y2 = y3 - t * cos (th1);
227 s_path_curveto (ctx->path,
228 a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
229 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
230 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
235 * s_path_arc: Add an arc to the path context.
236 * @ctx: Path context.
237 * @rx: Radius in x direction (before rotation).
238 * @ry: Radius in y direction (before rotation).
239 * @x_axis_rotation: Rotation angle for axes.
240 * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
241 * @sweep: 0 for "negative angle", 1 for "positive angle".
242 * @x: New x coordinate.
243 * @y: New y coordinate.
246 static void s_path_arc (RSVGParsePathCtx * ctx,
247 double rx, double ry, double x_axis_rotation,
248 int large_arc_flag, int sweep_flag, double x, double y)
250 double sin_th, cos_th;
251 double a00, a01, a10, a11;
252 double x0, y0, x1, y1, xc, yc;
253 double d, sfactor, sfactor_sq;
254 double th0, th1, th_arc;
255 int i, n_segs;
257 /* Check that neither radius is zero, since its isn't either
258 geometrically or mathematically meaningful and will
259 cause divide by zero and subsequent NaNs. We should
260 really do some ranged check ie -0.001 < x < 000.1 rather
261 can just a straight check again zero.
263 if ((rx == 0.0) || (ry == 0.0))
264 return;
266 sin_th = sin (x_axis_rotation * (M_PI / 180.0));
267 cos_th = cos (x_axis_rotation * (M_PI / 180.0));
268 a00 = cos_th / rx;
269 a01 = sin_th / rx;
270 a10 = -sin_th / ry;
271 a11 = cos_th / ry;
272 x0 = a00 * ctx->cpx + a01 * ctx->cpy;
273 y0 = a10 * ctx->cpx + a11 * ctx->cpy;
274 x1 = a00 * x + a01 * y;
275 y1 = a10 * x + a11 * y;
276 /* (x0, y0) is current point in transformed coordinate space.
277 (x1, y1) is new point in transformed coordinate space.
279 The arc fits a unit-radius circle in this space.
281 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
282 sfactor_sq = 1.0 / d - 0.25;
283 if (sfactor_sq < 0)
284 sfactor_sq = 0;
285 sfactor = sqrt (sfactor_sq);
286 if (sweep_flag == large_arc_flag)
287 sfactor = -sfactor;
288 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
289 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
290 /* (xc, yc) is center of the circle. */
292 th0 = atan2 (y0 - yc, x0 - xc);
293 th1 = atan2 (y1 - yc, x1 - xc);
295 th_arc = th1 - th0;
296 if (th_arc < 0 && sweep_flag)
297 th_arc += 2 * M_PI;
298 else if (th_arc > 0 && !sweep_flag)
299 th_arc -= 2 * M_PI;
301 n_segs = ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
303 for (i = 0; i < n_segs; i++)
304 s_path_arc_segment (ctx, xc, yc,
305 th0 + i * th_arc / n_segs,
306 th0 + (i + 1) * th_arc / n_segs, rx, ry, x_axis_rotation);
308 ctx->cpx = x;
309 ctx->cpy = y;
313 /* supply defaults for missing parameters, assuming relative coordinates
314 are to be interpreted as x,y */
315 static void s_path_parse_default_xy (RSVGParsePathCtx * ctx, int n_params)
317 int i;
319 if (ctx->rel) {
320 for (i = ctx->param; i < n_params; i++) {
321 if (i > 2)
322 ctx->params[i] = ctx->params[i - 2];
323 else if (i == 1)
324 ctx->params[i] = ctx->cpy;
325 else if (i == 0)
326 /* we shouldn't get here (usually ctx->param > 0 as
327 precondition) */
328 ctx->params[i] = ctx->cpx;
330 } else {
331 for (i = ctx->param; i < n_params; i++)
332 ctx->params[i] = 0.0;
337 static void s_path_parse_do_cmd (RSVGParsePathCtx * ctx, gboolean final)
339 double x1, y1, x2, y2, x3, y3;
341 switch (ctx->cmd) {
342 case 'm':
343 /* moveto */
344 if (ctx->param == 2 || final) {
345 s_path_parse_default_xy (ctx, 2);
346 s_path_moveto (ctx->path, ctx->params[0], ctx->params[1]);
347 ctx->mpx = ctx->cpx = ctx->rpx = ctx->params[0];
348 ctx->mpy = ctx->cpy = ctx->rpy = ctx->params[1];
349 ctx->param = 0;
350 ctx->cmd = 'l'; /* implicit linetos after a moveto */
352 break;
353 case 'l':
354 /* lineto */
355 if (ctx->param == 2 || final) {
356 s_path_parse_default_xy (ctx, 2);
357 s_path_lineto (ctx->path, ctx->params[0], ctx->params[1]);
358 ctx->cpx = ctx->rpx = ctx->params[0];
359 ctx->cpy = ctx->rpy = ctx->params[1];
360 ctx->param = 0;
362 break;
363 case 'c':
364 /* curveto */
365 if (ctx->param == 6 || final) {
366 s_path_parse_default_xy (ctx, 6);
367 x1 = ctx->params[0];
368 y1 = ctx->params[1];
369 x2 = ctx->params[2];
370 y2 = ctx->params[3];
371 x3 = ctx->params[4];
372 y3 = ctx->params[5];
373 s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
374 ctx->rpx = x2;
375 ctx->rpy = y2;
376 ctx->cpx = x3;
377 ctx->cpy = y3;
378 ctx->param = 0;
380 break;
381 case 's':
382 /* smooth curveto */
383 if (ctx->param == 4 || final) {
384 s_path_parse_default_xy (ctx, 4);
385 x1 = 2 * ctx->cpx - ctx->rpx;
386 y1 = 2 * ctx->cpy - ctx->rpy;
387 x2 = ctx->params[0];
388 y2 = ctx->params[1];
389 x3 = ctx->params[2];
390 y3 = ctx->params[3];
391 s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
392 ctx->rpx = x2;
393 ctx->rpy = y2;
394 ctx->cpx = x3;
395 ctx->cpy = y3;
396 ctx->param = 0;
398 break;
399 case 'h':
400 /* horizontal lineto */
401 if (ctx->param == 1) {
402 s_path_lineto (ctx->path, ctx->params[0], ctx->cpy);
403 ctx->cpx = ctx->rpx = ctx->params[0];
404 ctx->param = 0;
406 break;
407 case 'v':
408 /* vertical lineto */
409 if (ctx->param == 1) {
410 s_path_lineto (ctx->path, ctx->cpx, ctx->params[0]);
411 ctx->cpy = ctx->rpy = ctx->params[0];
412 ctx->param = 0;
414 break;
415 case 'q':
416 /* quadratic bezier curveto */
418 /* non-normative reference:
419 http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
421 if (ctx->param == 4 || final) {
422 s_path_parse_default_xy (ctx, 4);
423 /* raise quadratic bezier to cubic */
424 x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
425 y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
426 x3 = ctx->params[2];
427 y3 = ctx->params[3];
428 x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
429 y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
430 s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
431 ctx->rpx = ctx->params[0];
432 ctx->rpy = ctx->params[1];
433 ctx->cpx = x3;
434 ctx->cpy = y3;
435 ctx->param = 0;
437 break;
438 case 't':
439 /* Truetype quadratic bezier curveto */
440 if (ctx->param == 2 || final) {
441 double xc, yc; /* quadratic control point */
443 xc = 2 * ctx->cpx - ctx->rpx;
444 yc = 2 * ctx->cpy - ctx->rpy;
445 /* generate a quadratic bezier with control point = xc, yc */
446 x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
447 y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
448 x3 = ctx->params[0];
449 y3 = ctx->params[1];
450 x2 = (x3 + 2 * xc) * (1.0 / 3.0);
451 y2 = (y3 + 2 * yc) * (1.0 / 3.0);
452 s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
453 ctx->rpx = xc;
454 ctx->rpy = yc;
455 ctx->cpx = x3;
456 ctx->cpy = y3;
457 ctx->param = 0;
458 } else if (final) {
459 if (ctx->param > 2) {
460 s_path_parse_default_xy (ctx, 4);
461 /* raise quadratic bezier to cubic */
462 x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
463 y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
464 x3 = ctx->params[2];
465 y3 = ctx->params[3];
466 x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
467 y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
468 s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
469 ctx->rpx = ctx->params[0];
470 ctx->rpy = ctx->params[1];
471 ctx->cpx = x3;
472 ctx->cpy = y3;
473 } else {
474 s_path_parse_default_xy (ctx, 2);
475 s_path_lineto (ctx->path, ctx->params[0], ctx->params[1]);
476 ctx->cpx = ctx->rpx = ctx->params[0];
477 ctx->cpy = ctx->rpy = ctx->params[1];
479 ctx->param = 0;
481 break;
482 case 'a':
483 if (ctx->param == 7 || final) {
484 s_path_arc (ctx,
485 ctx->params[0], ctx->params[1], ctx->params[2],
486 ctx->params[3], ctx->params[4], ctx->params[5], ctx->params[6]);
487 ctx->param = 0;
489 break;
490 default:
491 ctx->param = 0;
496 static void s_path_parse_data (RSVGParsePathCtx * ctx, const char *data)
498 int i = 0;
499 double val = 0;
500 char c = 0;
501 gboolean in_num = FALSE;
502 gboolean in_frac = FALSE;
503 gboolean in_exp = FALSE;
504 gboolean exp_wait_sign = FALSE;
505 int sign = 0;
506 int exp = 0;
507 int exp_sign = 0;
508 double frac = 0.0;
510 in_num = FALSE;
511 for (i = 0;; i++) {
512 c = data[i];
513 if (c >= '0' && c <= '9') {
514 /* digit */
515 if (in_num) {
516 if (in_exp) {
517 exp = (exp * 10) + c - '0';
518 exp_wait_sign = FALSE;
519 } else if (in_frac)
520 val += (frac *= 0.1) * (c - '0');
521 else
522 val = (val * 10) + c - '0';
523 } else {
524 in_num = TRUE;
525 in_frac = FALSE;
526 in_exp = FALSE;
527 exp = 0;
528 exp_sign = 1;
529 exp_wait_sign = FALSE;
530 val = c - '0';
531 sign = 1;
533 } else if (c == '.') {
534 if (!in_num) {
535 in_num = TRUE;
536 val = 0;
538 in_frac = TRUE;
539 frac = 1;
540 } else if ((c == 'E' || c == 'e') && in_num) {
541 in_exp = TRUE;
542 exp_wait_sign = TRUE;
543 exp = 0;
544 exp_sign = 1;
545 } else if ((c == '+' || c == '-') && in_exp) {
546 exp_sign = c == '+' ? 1 : -1;
547 } else if (in_num) {
548 /* end of number */
550 val *= sign * pow (10, exp_sign * exp);
551 if (ctx->rel) {
552 /* Handle relative coordinates. This switch statement attempts
553 to determine _what_ the coords are relative to. This is
554 underspecified in the 12 Apr working draft. */
555 switch (ctx->cmd) {
556 case 'l':
557 case 'm':
558 case 'c':
559 case 's':
560 case 'q':
561 case 't':
562 #ifndef RSVGV_RELATIVE
563 /* rule: even-numbered params are x-relative, odd-numbered
564 are y-relative */
565 if ((ctx->param & 1) == 0)
566 val += ctx->cpx;
567 else if ((ctx->param & 1) == 1)
568 val += ctx->cpy;
569 break;
570 #else
571 /* rule: even-numbered params are x-relative, odd-numbered
572 are y-relative */
573 if (ctx->param == 0 || (ctx->param % 2 == 0))
574 val += ctx->cpx;
575 else
576 val += ctx->cpy;
577 break;
578 #endif
579 case 'a':
580 /* rule: sixth and seventh are x and y, rest are not
581 relative */
582 if (ctx->param == 5)
583 val += ctx->cpx;
584 else if (ctx->param == 6)
585 val += ctx->cpy;
586 break;
587 case 'h':
588 /* rule: x-relative */
589 val += ctx->cpx;
590 break;
591 case 'v':
592 /* rule: y-relative */
593 val += ctx->cpy;
594 break;
597 ctx->params[ctx->param++] = val;
598 s_path_parse_do_cmd (ctx, FALSE);
600 in_num = FALSE;
603 if (c == '\0')
604 break;
605 else if ((c == '+' || c == '-') && !exp_wait_sign) {
606 sign = c == '+' ? 1 : -1;
607 val = 0;
608 in_num = TRUE;
609 in_frac = FALSE;
610 in_exp = FALSE;
611 exp = 0;
612 exp_sign = 1;
613 exp_wait_sign = FALSE;
614 } else if (c == 'z' || c == 'Z') {
615 if (ctx->param)
616 s_path_parse_do_cmd (ctx, TRUE);
617 /* s_path_closepath (ctx->path); */
618 /* s_path_lineto (ctx->path, ctx->mpx, ctx->mpy); */
619 s_path_art_finish (ctx->path);
621 ctx->cpx = ctx->rpx = ctx->path->sections[ctx->path->num_sections - 1].x3;
622 ctx->cpy = ctx->rpy = ctx->path->sections[ctx->path->num_sections - 1].y3;
623 } else if (c >= 'A' && c <= 'Z' && c != 'E') {
624 if (ctx->param)
625 s_path_parse_do_cmd (ctx, TRUE);
626 ctx->cmd = c + 'a' - 'A';
627 ctx->rel = FALSE;
628 } else if (c >= 'a' && c <= 'z' && c != 'e') {
629 if (ctx->param)
630 s_path_parse_do_cmd (ctx, TRUE);
631 ctx->cmd = c;
632 ctx->rel = TRUE;
634 /* else c _should_ be whitespace or , */
639 PATH *s_path_parse (const char *path_str)
641 RSVGParsePathCtx ctx;
643 ctx.path = s_path_new ();
644 ctx.cpx = 0.0;
645 ctx.cpy = 0.0;
646 ctx.mpx = 0.0;
647 ctx.mpy = 0.0;
648 ctx.cmd = 0;
649 ctx.param = 0;
651 s_path_parse_data (&ctx, path_str);
653 if (ctx.param)
654 s_path_parse_do_cmd (&ctx, TRUE);
656 return ctx.path;
660 char *s_path_string_from_path (const PATH *path)
662 PATH_SECTION *section;
663 GString *path_string;
664 int i;
666 path_string = g_string_new ("");
668 for (i = 0; i < path->num_sections; i++) {
669 section = &path->sections[i];
671 if (i > 0)
672 g_string_append_c (path_string, '\n');
674 switch (section->code) {
675 case PATH_MOVETO:
676 g_string_append_printf (path_string, "M %i,%i",
677 section->x3, section->y3);
678 break;
679 case PATH_MOVETO_OPEN:
680 g_string_append_printf (path_string, "M %i,%i",
681 section->x3, section->y3);
682 break;
683 case PATH_CURVETO:
684 g_string_append_printf (path_string, "C %i,%i %i,%i %i,%i",
685 section->x1, section->y1,
686 section->x2, section->y2,
687 section->x3, section->y3);
688 break;
689 case PATH_LINETO:
690 g_string_append_printf (path_string, "L %i,%i",
691 section->x3, section->y3);
692 break;
693 case PATH_END:
694 g_string_append_printf (path_string, "z");
695 break;
699 return g_string_free (path_string, FALSE);
702 /*! \brief Converts a path to a polygon
704 * \param path [in] The path to convert to a polygon. This parameter must not
705 * be NULL.
706 * \param points [out] An array of the polygon's vertices. This parameter
707 * must not be NULL.
708 * \return TRUE if the path is closed, FALSE if it is open.
710 int s_path_to_polygon (PATH *path, GArray *points)
712 int closed = FALSE;
713 int i;
714 sPOINT point = { 0, 0 };
716 if (points->len > 0) {
717 g_array_remove_range (points, 0, points->len - 1);
720 for (i = 0; i < path->num_sections; i++) {
721 BEZIER bezier;
722 PATH_SECTION *section = &path->sections[i];
724 switch (section->code) {
725 case PATH_CURVETO:
726 bezier.x[0] = point.x;
727 bezier.y[0] = point.y;
728 bezier.x[1] = section->x1;
729 bezier.y[1] = section->y1;
730 bezier.x[2] = section->x2;
731 bezier.y[2] = section->y2;
732 point.x = bezier.x[3] = section->x3;
733 point.y = bezier.y[3] = section->y3;
734 m_polygon_append_bezier (points, &bezier, NUM_BEZIER_SEGMENTS);
735 break;
737 case PATH_MOVETO_OPEN:
738 /* Unsupported, just fall through and draw a line */
739 /* Fall through */
741 case PATH_MOVETO:
742 case PATH_LINETO:
743 point.x = section->x3;
744 point.y = section->y3;
745 m_polygon_append_point (points, point.x, point.y);
746 break;
748 case PATH_END:
749 closed = TRUE;
750 break;
754 return closed;
758 /*! \brief Calculates the distance between the given point and the closest
759 * point on the given path segment.
761 * \param [in] path The path.
762 * \param [in] x The x coordinate of the given point.
763 * \param [in] y The y coordinate of the given point.
764 * \param [in] solid TRUE if the path should be treated as solid, FALSE if
765 * the path should be treated as hollow.
766 * \return The shortest distance from the path to the point. With a solid
767 * shape, this function returns a distance of zero for interior points. With
768 * an invalid parameter, this function returns G_MAXDOUBLE.
770 double s_path_shortest_distance (PATH *path, int x, int y, int solid)
772 double shortest_distance = G_MAXDOUBLE;
773 int closed;
774 GArray *points;
776 points = g_array_new (FALSE, FALSE, sizeof (sPOINT));
778 closed = s_path_to_polygon (path, points);
780 if (!solid) {
781 shortest_distance = m_polygon_shortest_distance (points, x, y, closed);
783 } else if (m_polygon_interior_point (points, x, y)) {
784 shortest_distance = 0;
786 } else {
787 shortest_distance = m_polygon_shortest_distance (points, x, y, TRUE);
790 g_array_free (points, TRUE);
792 return shortest_distance;