1 /* Lepton EDA library
2  * Copyright (C) 1998-2016 gEDA Contributors
3  * Copyright (C) 2017-2021 Lepton EDA Contributors
4  *
5  * Code originally from librsvg 2.22.2 (LGPL) Copyright (C) 2000 Eazel, Inc.
6  *
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.
11  *
12  *  Adapted for gEDA by Peter Clifton <pcjc2@cam.ac.uk>
13  *
14  *  THIS FILE IS LGPL LICENSED, gEDA AS A WHOLE IS GPL LICENSED
15  *
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.
20  *
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.
25  *
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., 51 Franklin Street, Fifth Floor,
29  *  Boston, MA 02110-1301 USA
30  *
31  */
32 /*! \file path.c
33  */
34 
35 #include "config.h"
36 
37 #include <stdio.h>
38 #include <math.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #include <glib.h>
43 
44 #include "liblepton_priv.h"
45 
46 #define NUM_BEZIER_SEGMENTS 100
47 
48 
49 /*! \brief Create a new LeptonPath structure.
50  *
51  *  \return The newly created LeptonPath structure.
52  */
53 LeptonPath*
lepton_path_new()54 lepton_path_new ()
55 {
56   LeptonPath *path;
57 
58   path = g_new (LeptonPath, 1);
59   path->num_sections = 0;
60   path->num_sections_max = 16;
61   path->sections = g_new (LeptonPathSection, path->num_sections_max);
62 
63   return path;
64 }
65 
66 
67 /*! \brief Free memory associated with the LeptonPath object.
68  *
69  *  \param [in] LeptonPath object to be freed.
70  */
71 void
lepton_path_free(LeptonPath * path)72 lepton_path_free (LeptonPath * path)
73 {
74   if (path != NULL) {
75     g_free (path->sections);
76     g_free (path);
77   }
78 }
79 
80 /*! \brief Get the code type of path section.
81  *  \par Function Description
82  *  This function returns the PATH_CODE name of a
83  *  #LeptonPathSection object.
84  *
85  *  \param [in] section The #LeptonPathSection object.
86  *  \return PATH_CODE enum value of the path section object.
87  */
88 PATH_CODE
lepton_path_section_get_code(LeptonPathSection * section)89 lepton_path_section_get_code (LeptonPathSection *section)
90 {
91   return section->code;
92 }
93 
94 /*! \brief Get X coordinate of the first point of path section.
95  *  \par Function Description
96  *  Given a #LeptonPathSection variable, this getter returns the X
97  *  coordinate of its first point.
98  *
99  *  \param [in] section The #LeptonPathSection variable.
100  *  \return The X coordinate value of the first point of the path
101  *  section.
102  */
103 int
lepton_path_section_get_x1(LeptonPathSection * section)104 lepton_path_section_get_x1 (LeptonPathSection *section)
105 {
106   return section->x1;
107 }
108 
109 /*! \brief Get Y coordinate of the first point of path section.
110  *  \par Function Description
111  *  Given a #LeptonPathSection variable, this getter returns the Y
112  *  coordinate of its first point.
113  *
114  *  \param [in] section The #LeptonPathSection variable.
115  *  \return The Y coordinate value of the first point of the path
116  *  section.
117  */
118 int
lepton_path_section_get_y1(LeptonPathSection * section)119 lepton_path_section_get_y1 (LeptonPathSection *section)
120 {
121   return section->y1;
122 }
123 
124 /*! \brief Get X coordinate of the second point of path section.
125  *  \par Function Description
126  *  Given a #LeptonPathSection variable, this getter returns the X
127  *  coordinate of its second point.
128  *
129  *  \param [in] section The #LeptonPathSection variable.
130  *  \return The X coordinate value of the second point of the path
131  *  section.
132  */
133 int
lepton_path_section_get_x2(LeptonPathSection * section)134 lepton_path_section_get_x2 (LeptonPathSection *section)
135 {
136   return section->x2;
137 }
138 
139 /*! \brief Get Y coordinate of the second point of path section.
140  *  \par Function Description
141  *  Given a #LeptonPathSection variable, this getter returns the Y
142  *  coordinate of its second point.
143  *
144  *  \param [in] section The #LeptonPathSection variable.
145  *  \return The Y coordinate value of the second point of the path
146  *  section.
147  */
148 int
lepton_path_section_get_y2(LeptonPathSection * section)149 lepton_path_section_get_y2 (LeptonPathSection *section)
150 {
151   return section->y2;
152 }
153 
154 /*! \brief Get X coordinate of the third point of path section.
155  *  \par Function Description
156  *  Given a #LeptonPathSection variable, this getter returns the X
157  *  coordinate of its third point.
158  *
159  *  \param [in] section The #LeptonPathSection variable.
160  *  \return The X coordinate value of the third point of the path
161  *  section.
162  */
163 int
lepton_path_section_get_x3(LeptonPathSection * section)164 lepton_path_section_get_x3 (LeptonPathSection *section)
165 {
166   return section->x3;
167 }
168 
169 /*! \brief Get Y coordinate of the third point of path section.
170  *  \par Function Description
171  *  Given a #LeptonPathSection variable, this getter returns the Y
172  *  coordinate of its third point.
173  *
174  *  \param [in] section The #LeptonPathSection variable.
175  *  \return The Y coordinate value of the third point of the path
176  *  section.
177  */
178 int
lepton_path_section_get_y3(LeptonPathSection * section)179 lepton_path_section_get_y3 (LeptonPathSection *section)
180 {
181   return section->y3;
182 }
183 
184 /*! \brief Return a path section code enum value from a string.
185  * \par Function Description
186  * Given a string \a s, returns the #PATH_CODE enum value
187  * corresponding to it.  This is mainly intended to be used for
188  * value conversion in Scheme FFI functions.
189  *
190  * \param [in] s The string.
191  */
192 int
lepton_path_section_code_from_string(char * s)193 lepton_path_section_code_from_string (char *s)
194 {
195   int result = PATH_END;
196 
197   if      (strcmp (s, "moveto") == 0) { result = PATH_MOVETO; }
198   else if (strcmp (s, "lineto") == 0) { result = PATH_LINETO; }
199   else if (strcmp (s, "curveto") == 0) {result = PATH_CURVETO; }
200   else if (strcmp (s, "closepath") == 0) {result = PATH_END; }
201 
202   return result;
203 }
204 
205 /*! \brief Return a string holding the representation of #PATH_CODE value.
206  * \par Function Description
207  * Given a #PATH_CODE value, returns its external representation
208  * as a string.  This is mainly intended to be used in Scheme FFI
209  * functions.
210  *
211  *  \param [in] code The #PATH_CODE value.
212  */
213 const char*
lepton_path_section_code_to_string(int code)214 lepton_path_section_code_to_string (int code)
215 {
216   const char *result = NULL;
217 
218   switch (code)
219   {
220   case PATH_MOVETO:
221   case PATH_MOVETO_OPEN: result = "moveto"; break;
222   case PATH_LINETO: result = "lineto"; break;
223   case PATH_CURVETO: result = "curveto"; break;
224   case PATH_END: result = "closepath"; break;
225   default: break;
226   }
227 
228   return result;
229 }
230 
231 /*! \brief Create a new "moveto" section in #LeptonPath object.
232  *  \par Function Description
233  *  The function modifies #LeptonPath object by adding a new
234  *  "moveto" section with given coordinates x and y.  If the last
235  *  section of the path is already a "moveto" one, amend it
236  *  instead of adding a new section.
237  *
238  *  \param [in,out] path The #LeptonPath object.
239  *  \param [in] x The X coordinate of the "moveto" point.
240  *  \param [in] y The Y coordinate of the "moveto" point.
241  */
242 void
lepton_path_moveto(LeptonPath * path,double x,double y)243 lepton_path_moveto (LeptonPath *path,
244                     double x,
245                     double y)
246 {
247   LeptonPathSection *sections;
248   int num_sections;
249 
250   g_return_if_fail (path != NULL);
251 
252   /* if the last command was a moveto then change that last moveto instead of
253      creating a new one */
254   sections = path->sections;
255   num_sections = path->num_sections;
256 
257   if (num_sections > 0)
258     if (sections[num_sections - 1].code == PATH_MOVETO_OPEN) {
259       sections[num_sections - 1].x3 = x;
260       sections[num_sections - 1].y3 = y;
261       return;
262     }
263 
264   num_sections = path->num_sections++;
265 
266   if (num_sections == path->num_sections_max)
267     path->sections =
268       (LeptonPathSection*) g_realloc (path->sections,
269                                       (path->num_sections_max <<= 1) *
270                                       sizeof (LeptonPathSection));
271   sections = path->sections;
272   sections[num_sections].code = PATH_MOVETO_OPEN;
273   sections[num_sections].x3 = x;
274   sections[num_sections].y3 = y;
275 }
276 
277 
278 /*! \brief Create a new "lineto" section in #LeptonPath object.
279  *  \par Function Description
280  *  The function modifies #LeptonPath object by adding a new
281  *  "lineto" section with given coordinates x and y.
282  *
283  *  \param [in,out] path The #LeptonPath object.
284  *  \param [in] x The X coordinate of the "lineto" point.
285  *  \param [in] y The Y coordinate of the "lineto" point.
286  */
287 void
lepton_path_lineto(LeptonPath * path,double x,double y)288 lepton_path_lineto (LeptonPath *path,
289                     double x,
290                     double y)
291 {
292   LeptonPathSection *sections;
293   int num_sections;
294 
295   g_return_if_fail (path != NULL);
296 
297   num_sections = path->num_sections++;
298 
299   if (num_sections == path->num_sections_max)
300     path->sections =
301       (LeptonPathSection*) g_realloc (path->sections,
302                                       (path->num_sections_max <<= 1) *
303                                       sizeof (LeptonPathSection));
304   sections = path->sections;
305   sections[num_sections].code = PATH_LINETO;
306   sections[num_sections].x3 = x;
307   sections[num_sections].y3 = y;
308 }
309 
310 
311 /*! \brief Create a new "curveto" section in #LeptonPath object.
312  *  \par Function Description
313  *  The function modifies #LeptonPath object by adding a new
314  *  "curveto" section with given coordinates of three control
315  *  points.
316  *
317  *  \param [in,out] path The #LeptonPath object.
318  *  \param [in] x1 The X coordinate of the first control point.
319  *  \param [in] y1 The Y coordinate of the first control point.
320  *  \param [in] x2 The X coordinate of the second control point.
321  *  \param [in] y2 The Y coordinate of the second control point.
322  *  \param [in] x3 The X coordinate of the third control point.
323  *  \param [in] y3 The Y coordinate of the third control point.
324  */
325 void
lepton_path_curveto(LeptonPath * path,double x1,double y1,double x2,double y2,double x3,double y3)326 lepton_path_curveto (LeptonPath *path,
327                      double x1,
328                      double y1,
329                      double x2,
330                      double y2,
331                      double x3,
332                      double y3)
333 {
334   LeptonPathSection *sections;
335   int num_sections;
336 
337   g_return_if_fail (path != NULL);
338 
339   num_sections = path->num_sections++;
340 
341   if (num_sections == path->num_sections_max)
342     path->sections =
343       (LeptonPathSection*) g_realloc (path->sections,
344                                       (path->num_sections_max <<= 1) *
345                                       sizeof (LeptonPathSection));
346   sections = path->sections;
347   sections[num_sections].code = PATH_CURVETO;
348   sections[num_sections].x1 = x1;
349   sections[num_sections].y1 = y1;
350   sections[num_sections].x2 = x2;
351   sections[num_sections].y2 = y2;
352   sections[num_sections].x3 = x3;
353   sections[num_sections].y3 = y3;
354 }
355 
356 
357 /*! \brief Finish #LeptonPath object creation.
358  *  \par Function Description
359  *  Finishes #LeptonPath object creation by adding a last ending
360  *  section.
361  *
362  *  \param [in,out] path The #LeptonPath object.
363  */
364 void
lepton_path_art_finish(LeptonPath * path)365 lepton_path_art_finish (LeptonPath * path)
366 {
367   int num_sections;
368 
369   g_return_if_fail (path != NULL);
370 
371   num_sections = path->num_sections++;
372 
373   if (num_sections == path->num_sections_max)
374     path->sections =
375       (LeptonPathSection*) g_realloc (path->sections,
376                                       (path->num_sections_max <<= 1) *
377                                       sizeof (LeptonPathSection));
378   path->sections[num_sections].code = PATH_END;
379 }
380 
381 
382 /* This module parses an SVG style path element into a LeptonPath.
383 
384   At present, there is no support for <marker> or any other contextual
385   information from the SVG file. The API will need to change rather
386   significantly to support these.
387 
388   Reference: SVG working draft 3 March 2000, section 8.
389 */
390 
391 typedef struct _RSVGParsePathCtx RSVGParsePathCtx;
392 
393 struct _RSVGParsePathCtx {
394   LeptonPath *path;
395   double cpx, cpy;    /* current point */
396   double rpx, rpy;    /* reflection point (for 's' and 't' commands) */
397   double mpx, mpy;    /* Last moved to point (for path closures) */
398   char cmd;           /* current command (lowercase) */
399   int param;          /* parameter number */
400   gboolean rel;       /* true if relative coords */
401   double params[7];   /* parameters that have been parsed */
402 };
403 
404 
405 static void
lepton_path_arc_segment(RSVGParsePathCtx * ctx,double xc,double yc,double th0,double th1,double rx,double ry,double x_axis_rotation)406 lepton_path_arc_segment (RSVGParsePathCtx * ctx,
407                          double xc,
408                          double yc,
409                          double th0,
410                          double th1,
411                          double rx,
412                          double ry,
413                          double x_axis_rotation)
414 {
415   double sin_th, cos_th;
416   double a00, a01, a10, a11;
417   double x1, y1, x2, y2, x3, y3;
418   double t;
419   double th_half;
420 
421   sin_th = sin (x_axis_rotation * (M_PI / 180.0));
422   cos_th = cos (x_axis_rotation * (M_PI / 180.0));
423   /* inverse transform compared with lepton_path_arc */
424   a00 = cos_th * rx;
425   a01 = -sin_th * ry;
426   a10 = sin_th * rx;
427   a11 = cos_th * ry;
428 
429   th_half = 0.5 * (th1 - th0);
430   t = (8.0 / 3.0) * sin (th_half * 0.5) * sin (th_half * 0.5) / sin (th_half);
431   x1 = xc + cos (th0) - t * sin (th0);
432   y1 = yc + sin (th0) + t * cos (th0);
433   x3 = xc + cos (th1);
434   y3 = yc + sin (th1);
435   x2 = x3 + t * sin (th1);
436   y2 = y3 - t * cos (th1);
437   lepton_path_curveto (ctx->path,
438                        a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
439                        a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
440                        a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
441 }
442 
443 
444 /*
445  * lepton_path_arc: Add an arc to the path context.
446  * @ctx: Path context.
447  * @rx: Radius in x direction (before rotation).
448  * @ry: Radius in y direction (before rotation).
449  * @x_axis_rotation: Rotation angle for axes.
450  * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
451  * @sweep: 0 for "negative angle", 1 for "positive angle".
452  * @x: New x coordinate.
453  * @y: New y coordinate.
454  *
455  */
456 static void
lepton_path_arc(RSVGParsePathCtx * ctx,double rx,double ry,double x_axis_rotation,int large_arc_flag,int sweep_flag,double x,double y)457 lepton_path_arc (RSVGParsePathCtx * ctx,
458                  double rx,
459                  double ry,
460                  double x_axis_rotation,
461                  int large_arc_flag,
462                  int sweep_flag,
463                  double x,
464                  double y)
465 {
466   double sin_th, cos_th;
467   double a00, a01, a10, a11;
468   double x0, y0, x1, y1, xc, yc;
469   double d, sfactor, sfactor_sq;
470   double th0, th1, th_arc;
471   int i, n_segs;
472 
473   /* Check that neither radius is zero, since its isn't either
474      geometrically or mathematically meaningful and will
475      cause divide by zero and subsequent NaNs.  We should
476      really do some ranged check ie -0.001 < x < 000.1 rather
477      can just a straight check again zero.
478    */
479   if ((rx == 0.0) || (ry == 0.0))
480     return;
481 
482   sin_th = sin (x_axis_rotation * (M_PI / 180.0));
483   cos_th = cos (x_axis_rotation * (M_PI / 180.0));
484   a00 = cos_th / rx;
485   a01 = sin_th / rx;
486   a10 = -sin_th / ry;
487   a11 = cos_th / ry;
488   x0 = a00 * ctx->cpx + a01 * ctx->cpy;
489   y0 = a10 * ctx->cpx + a11 * ctx->cpy;
490   x1 = a00 * x + a01 * y;
491   y1 = a10 * x + a11 * y;
492   /* (x0, y0) is current point in transformed coordinate space.
493      (x1, y1) is new point in transformed coordinate space.
494 
495      The arc fits a unit-radius circle in this space.
496    */
497   d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
498   sfactor_sq = 1.0 / d - 0.25;
499   if (sfactor_sq < 0)
500     sfactor_sq = 0;
501   sfactor = sqrt (sfactor_sq);
502   if (sweep_flag == large_arc_flag)
503     sfactor = -sfactor;
504   xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
505   yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
506   /* (xc, yc) is center of the circle. */
507 
508   th0 = atan2 (y0 - yc, x0 - xc);
509   th1 = atan2 (y1 - yc, x1 - xc);
510 
511   th_arc = th1 - th0;
512   if (th_arc < 0 && sweep_flag)
513     th_arc += 2 * M_PI;
514   else if (th_arc > 0 && !sweep_flag)
515     th_arc -= 2 * M_PI;
516 
517   n_segs = ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
518 
519   for (i = 0; i < n_segs; i++)
520     lepton_path_arc_segment (ctx,
521                              xc, yc,
522                              th0 + i * th_arc / n_segs,
523                              th0 + (i + 1) * th_arc / n_segs,
524                              rx, ry,
525                              x_axis_rotation);
526 
527   ctx->cpx = x;
528   ctx->cpy = y;
529 }
530 
531 
532 /* supply defaults for missing parameters, assuming relative coordinates
533    are to be interpreted as x,y */
534 static void
lepton_path_parse_default_xy(RSVGParsePathCtx * ctx,int n_params)535 lepton_path_parse_default_xy (RSVGParsePathCtx * ctx,
536                               int n_params)
537 {
538   int i;
539 
540   if (ctx->rel) {
541     for (i = ctx->param; i < n_params; i++) {
542       if (i > 2)
543         ctx->params[i] = ctx->params[i - 2];
544       else if (i == 1)
545         ctx->params[i] = ctx->cpy;
546       else if (i == 0)
547         /* we shouldn't get here (usually ctx->param > 0 as
548            precondition) */
549         ctx->params[i] = ctx->cpx;
550     }
551   } else {
552     for (i = ctx->param; i < n_params; i++)
553       ctx->params[i] = 0.0;
554   }
555 }
556 
557 
558 static void
lepton_path_parse_do_cmd(RSVGParsePathCtx * ctx,gboolean final)559 lepton_path_parse_do_cmd (RSVGParsePathCtx *ctx,
560                           gboolean final)
561 {
562   double x1, y1, x2, y2, x3, y3;
563 
564   switch (ctx->cmd) {
565   case 'm':
566     /* moveto */
567     if (ctx->param == 2 || final) {
568       lepton_path_parse_default_xy (ctx, 2);
569       lepton_path_moveto (ctx->path, ctx->params[0], ctx->params[1]);
570       ctx->mpx = ctx->cpx = ctx->rpx = ctx->params[0];
571       ctx->mpy = ctx->cpy = ctx->rpy = ctx->params[1];
572       ctx->param = 0;
573       ctx->cmd = 'l'; /* implicit linetos after a moveto */
574     }
575     break;
576   case 'l':
577     /* lineto */
578     if (ctx->param == 2 || final) {
579       lepton_path_parse_default_xy (ctx, 2);
580       lepton_path_lineto (ctx->path, ctx->params[0], ctx->params[1]);
581       ctx->cpx = ctx->rpx = ctx->params[0];
582       ctx->cpy = ctx->rpy = ctx->params[1];
583       ctx->param = 0;
584     }
585     break;
586   case 'c':
587     /* curveto */
588     if (ctx->param == 6 || final) {
589       lepton_path_parse_default_xy (ctx, 6);
590       x1 = ctx->params[0];
591       y1 = ctx->params[1];
592       x2 = ctx->params[2];
593       y2 = ctx->params[3];
594       x3 = ctx->params[4];
595       y3 = ctx->params[5];
596       lepton_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
597       ctx->rpx = x2;
598       ctx->rpy = y2;
599       ctx->cpx = x3;
600       ctx->cpy = y3;
601       ctx->param = 0;
602     }
603     break;
604   case 's':
605     /* smooth curveto */
606     if (ctx->param == 4 || final) {
607       lepton_path_parse_default_xy (ctx, 4);
608       x1 = 2 * ctx->cpx - ctx->rpx;
609       y1 = 2 * ctx->cpy - ctx->rpy;
610       x2 = ctx->params[0];
611       y2 = ctx->params[1];
612       x3 = ctx->params[2];
613       y3 = ctx->params[3];
614       lepton_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
615       ctx->rpx = x2;
616       ctx->rpy = y2;
617       ctx->cpx = x3;
618       ctx->cpy = y3;
619       ctx->param = 0;
620     }
621     break;
622   case 'h':
623     /* horizontal lineto */
624     if (ctx->param == 1) {
625       lepton_path_lineto (ctx->path, ctx->params[0], ctx->cpy);
626       ctx->cpx = ctx->rpx = ctx->params[0];
627       ctx->param = 0;
628     }
629     break;
630   case 'v':
631     /* vertical lineto */
632     if (ctx->param == 1) {
633       lepton_path_lineto (ctx->path, ctx->cpx, ctx->params[0]);
634       ctx->cpy = ctx->rpy = ctx->params[0];
635       ctx->param = 0;
636     }
637     break;
638   case 'q':
639     /* quadratic bezier curveto */
640 
641     /* non-normative reference:
642        http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
643      */
644     if (ctx->param == 4 || final) {
645       lepton_path_parse_default_xy (ctx, 4);
646       /* raise quadratic bezier to cubic */
647       x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
648       y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
649       x3 = ctx->params[2];
650       y3 = ctx->params[3];
651       x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
652       y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
653       lepton_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
654       ctx->rpx = ctx->params[0];
655       ctx->rpy = ctx->params[1];
656       ctx->cpx = x3;
657       ctx->cpy = y3;
658       ctx->param = 0;
659     }
660     break;
661   case 't':
662     /* Truetype quadratic bezier curveto */
663     if (ctx->param == 2 || final) {
664       double xc, yc;    /* quadratic control point */
665 
666       xc = 2 * ctx->cpx - ctx->rpx;
667       yc = 2 * ctx->cpy - ctx->rpy;
668       /* generate a quadratic bezier with control point = xc, yc */
669       x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
670       y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
671       x3 = ctx->params[0];
672       y3 = ctx->params[1];
673       x2 = (x3 + 2 * xc) * (1.0 / 3.0);
674       y2 = (y3 + 2 * yc) * (1.0 / 3.0);
675       lepton_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
676       ctx->rpx = xc;
677       ctx->rpy = yc;
678       ctx->cpx = x3;
679       ctx->cpy = y3;
680       ctx->param = 0;
681     } else if (final) {
682       if (ctx->param > 2) {
683         lepton_path_parse_default_xy (ctx, 4);
684         /* raise quadratic bezier to cubic */
685         x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
686         y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
687         x3 = ctx->params[2];
688         y3 = ctx->params[3];
689         x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
690         y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
691         lepton_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
692         ctx->rpx = ctx->params[0];
693         ctx->rpy = ctx->params[1];
694         ctx->cpx = x3;
695         ctx->cpy = y3;
696       } else {
697         lepton_path_parse_default_xy (ctx, 2);
698         lepton_path_lineto (ctx->path, ctx->params[0], ctx->params[1]);
699         ctx->cpx = ctx->rpx = ctx->params[0];
700         ctx->cpy = ctx->rpy = ctx->params[1];
701       }
702       ctx->param = 0;
703     }
704     break;
705   case 'a':
706     if (ctx->param == 7 || final) {
707       lepton_path_arc (ctx,
708                        ctx->params[0],
709                        ctx->params[1],
710                        ctx->params[2],
711                        ctx->params[3],
712                        ctx->params[4],
713                        ctx->params[5],
714                        ctx->params[6]);
715       ctx->param = 0;
716     }
717     break;
718   default:
719     ctx->param = 0;
720   }
721 }
722 
723 
724 static void
lepton_path_parse_data(RSVGParsePathCtx * ctx,const char * data)725 lepton_path_parse_data (RSVGParsePathCtx *ctx,
726                         const char *data)
727 {
728   int i = 0;
729   double val = 0;
730   char c = 0;
731   gboolean in_num = FALSE;
732   gboolean in_frac = FALSE;
733   gboolean in_exp = FALSE;
734   gboolean exp_wait_sign = FALSE;
735   int sign = 0;
736   int exp = 0;
737   int exp_sign = 0;
738   double frac = 0.0;
739 
740   in_num = FALSE;
741   for (i = 0;; i++) {
742     c = data[i];
743     if (c >= '0' && c <= '9') {
744       /* digit */
745       if (in_num) {
746         if (in_exp) {
747           exp = (exp * 10) + c - '0';
748           exp_wait_sign = FALSE;
749         } else if (in_frac)
750           val += (frac *= 0.1) * (c - '0');
751         else
752           val = (val * 10) + c - '0';
753       } else {
754         in_num = TRUE;
755         in_frac = FALSE;
756         in_exp = FALSE;
757         exp = 0;
758         exp_sign = 1;
759         exp_wait_sign = FALSE;
760         val = c - '0';
761         sign = 1;
762       }
763     } else if (c == '.') {
764       if (!in_num) {
765         in_num = TRUE;
766         val = 0;
767       }
768       in_frac = TRUE;
769       frac = 1;
770     } else if ((c == 'E' || c == 'e') && in_num) {
771       in_exp = TRUE;
772       exp_wait_sign = TRUE;
773       exp = 0;
774       exp_sign = 1;
775     } else if ((c == '+' || c == '-') && in_exp) {
776       exp_sign = c == '+' ? 1 : -1;
777     } else if (in_num) {
778       /* end of number */
779 
780       val *= sign * pow (10, exp_sign * exp);
781       if (ctx->rel) {
782         /* Handle relative coordinates. This switch statement attempts
783            to determine _what_ the coords are relative to. This is
784            underspecified in the 12 Apr working draft. */
785         switch (ctx->cmd) {
786         case 'l':
787         case 'm':
788         case 'c':
789         case 's':
790         case 'q':
791         case 't':
792 #ifndef RSVGV_RELATIVE
793           /* rule: even-numbered params are x-relative, odd-numbered
794              are y-relative */
795           if ((ctx->param & 1) == 0)
796             val += ctx->cpx;
797           else if ((ctx->param & 1) == 1)
798             val += ctx->cpy;
799           break;
800 #else
801           /* rule: even-numbered params are x-relative, odd-numbered
802              are y-relative */
803           if (ctx->param == 0 || (ctx->param % 2 == 0))
804             val += ctx->cpx;
805           else
806             val += ctx->cpy;
807           break;
808 #endif
809         case 'a':
810           /* rule: sixth and seventh are x and y, rest are not
811              relative */
812           if (ctx->param == 5)
813             val += ctx->cpx;
814           else if (ctx->param == 6)
815             val += ctx->cpy;
816           break;
817         case 'h':
818           /* rule: x-relative */
819           val += ctx->cpx;
820           break;
821         case 'v':
822           /* rule: y-relative */
823           val += ctx->cpy;
824           break;
825         }
826       }
827       ctx->params[ctx->param++] = val;
828       lepton_path_parse_do_cmd (ctx, FALSE);
829 
830       in_num = FALSE;
831     }
832 
833     if (c == '\0')
834       break;
835     else if ((c == '+' || c == '-') && !exp_wait_sign) {
836       sign = c == '+' ? 1 : -1;
837       val = 0;
838       in_num = TRUE;
839       in_frac = FALSE;
840       in_exp = FALSE;
841       exp = 0;
842       exp_sign = 1;
843       exp_wait_sign = FALSE;
844     } else if (c == 'z' || c == 'Z') {
845       if (ctx->param)
846         lepton_path_parse_do_cmd (ctx, TRUE);
847       /* s_path_closepath (ctx->path); */
848       /* lepton_path_lineto (ctx->path, ctx->mpx, ctx->mpy); */
849       lepton_path_art_finish (ctx->path);
850 
851       ctx->cpx = ctx->rpx = ctx->path->sections[ctx->path->num_sections - 1].x3;
852       ctx->cpy = ctx->rpy = ctx->path->sections[ctx->path->num_sections - 1].y3;
853     } else if (c >= 'A' && c <= 'Z' && c != 'E') {
854       if (ctx->param)
855         lepton_path_parse_do_cmd (ctx, TRUE);
856       ctx->cmd = c + 'a' - 'A';
857       ctx->rel = FALSE;
858     } else if (c >= 'a' && c <= 'z' && c != 'e') {
859       if (ctx->param)
860         lepton_path_parse_do_cmd (ctx, TRUE);
861       ctx->cmd = c;
862       ctx->rel = TRUE;
863     }
864     /* else c _should_ be whitespace or , */
865   }
866 }
867 
868 
869 /*! \brief Return #LeptonPath pointer object from path string.
870  *  \par Function Description
871  *  Given a path string representation in gEDA file format creates
872  *  a #LeptonPath object corresponding to it and returns the
873  *  pointer to the object.
874  *
875  *  \param path_str [in] The representation of path as a string.
876  *  \return The #LeptonPath object pointer.
877  */
878 LeptonPath*
lepton_path_parse(const char * path_str)879 lepton_path_parse (const char *path_str)
880 {
881   RSVGParsePathCtx ctx;
882 
883   ctx.path = lepton_path_new ();
884   ctx.cpx = 0.0;
885   ctx.cpy = 0.0;
886   ctx.mpx = 0.0;
887   ctx.mpy = 0.0;
888   ctx.cmd = 0;
889   ctx.param = 0;
890 
891   lepton_path_parse_data (&ctx, path_str);
892 
893   if (ctx.param)
894     lepton_path_parse_do_cmd (&ctx, TRUE);
895 
896   return ctx.path;
897 }
898 
899 
900 /*! \brief Return string representation of #LeptonPath object.
901  *  \par Function Description
902  *  Given a #LeptonPath object pointer, returns its representation
903  *  as a string in gEDA file format.
904  *
905  *  \param path [in] The #LeptonPath object pointer.
906  *  \return The string representing the object.
907  */
908 char*
lepton_path_string_from_path(const LeptonPath * path)909 lepton_path_string_from_path (const LeptonPath *path)
910 {
911   LeptonPathSection *section;
912   GString *path_string;
913   int i;
914 
915   path_string = g_string_new ("");
916 
917   for (i = 0; i < path->num_sections; i++) {
918     section = &path->sections[i];
919 
920     if (i > 0)
921       g_string_append_c (path_string, '\n');
922 
923     switch (section->code) {
924       case PATH_MOVETO:
925         g_string_append_printf (path_string, "M %i,%i",
926                                 section->x3, section->y3);
927         break;
928       case PATH_MOVETO_OPEN:
929         g_string_append_printf (path_string, "M %i,%i",
930                                 section->x3, section->y3);
931         break;
932       case PATH_CURVETO:
933         g_string_append_printf (path_string, "C %i,%i %i,%i %i,%i",
934                                 section->x1, section->y1,
935                                 section->x2, section->y2,
936                                 section->x3, section->y3);
937         break;
938       case PATH_LINETO:
939         g_string_append_printf (path_string, "L %i,%i",
940                                 section->x3, section->y3);
941         break;
942       case PATH_END:
943         g_string_append_printf (path_string, "z");
944         break;
945     }
946   }
947 
948   return g_string_free (path_string, FALSE);
949 }
950 
951 /*! \brief Converts a path to a polygon
952  *
953  *  \param path [in] The path to convert to a polygon.  This parameter must not
954  *  be NULL.
955  *  \param points [out] An array of the polygon's vertices.  This parameter
956  *  must not be NULL.
957  *  \return TRUE if the path is closed, FALSE if it is open.
958  */
959 int
lepton_path_to_polygon(LeptonPath * path,GArray * points)960 lepton_path_to_polygon (LeptonPath *path,
961                         GArray *points)
962 {
963   int closed = FALSE;
964   int i;
965   LeptonPoint point = { 0, 0 };
966 
967   if (points->len > 0) {
968     g_array_remove_range (points, 0, points->len - 1);
969   }
970 
971   for (i = 0; i < path->num_sections; i++) {
972     LeptonBezier bezier;
973     LeptonPathSection *section = &path->sections[i];
974 
975     switch (section->code) {
976       case PATH_CURVETO:
977         bezier.x[0] = point.x;
978         bezier.y[0] = point.y;
979         bezier.x[1] = section->x1;
980         bezier.y[1] = section->y1;
981         bezier.x[2] = section->x2;
982         bezier.y[2] = section->y2;
983         point.x = bezier.x[3] = section->x3;
984         point.y = bezier.y[3] = section->y3;
985         m_polygon_append_bezier (points, &bezier, NUM_BEZIER_SEGMENTS);
986         break;
987 
988       case PATH_MOVETO_OPEN:
989         /* Unsupported, just fall through and draw a line */
990         /* Fall through */
991 
992       case PATH_MOVETO:
993       case PATH_LINETO:
994         point.x = section->x3;
995         point.y = section->y3;
996         m_polygon_append_point (points, point.x, point.y);
997         break;
998 
999       case PATH_END:
1000         closed = TRUE;
1001         break;
1002     }
1003   }
1004 
1005   return closed;
1006 }
1007 
1008 
1009 /*! \brief Calculates the distance between the given point and the closest
1010  *  point on the given path segment.
1011  *
1012  *  \param [in] path    The path.
1013  *  \param [in] x       The x coordinate of the given point.
1014  *  \param [in] y       The y coordinate of the given point.
1015  *  \param [in] solid   TRUE if the path should be treated as solid, FALSE if
1016  *  the path should be treated as hollow.
1017  *  \return The shortest distance from the path to the point.  With a solid
1018  *  shape, this function returns a distance of zero for interior points.  With
1019  *  an invalid parameter, this function returns G_MAXDOUBLE.
1020  */
1021 double
lepton_path_shortest_distance(LeptonPath * path,int x,int y,int solid)1022 lepton_path_shortest_distance (LeptonPath *path,
1023                                int x,
1024                                int y,
1025                                int solid)
1026 {
1027   double shortest_distance = G_MAXDOUBLE;
1028   int closed;
1029   GArray *points;
1030 
1031   points = g_array_new (FALSE, FALSE, sizeof (LeptonPoint));
1032 
1033   closed = lepton_path_to_polygon (path, points);
1034 
1035   if (!solid) {
1036     shortest_distance = m_polygon_shortest_distance (points, x, y, closed);
1037 
1038   } else if (m_polygon_interior_point (points, x, y)) {
1039     shortest_distance = 0;
1040 
1041   } else {
1042     shortest_distance = m_polygon_shortest_distance (points, x, y, TRUE);
1043   }
1044 
1045   g_array_free (points, TRUE);
1046 
1047   return shortest_distance;
1048 }
1049