1 /* Libart_LGPL - library of basic graphic primitives
2 * Copyright (C) 1998-2000 Raph Levien
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
22 /* Vector path set operations, over sorted vpaths. */
25 #include "art_svp_ops.h"
30 #include "art_vpath.h"
31 #include "art_svp_vpath.h"
33 #ifdef ART_USE_NEW_INTERSECTOR
34 #include "art_svp_intersect.h"
36 #include "art_svp_wind.h"
38 #include "art_vpath_svp.h"
40 /* Merge the segments of the two svp's. The resulting svp will share
41 segments with args passed in, so be super-careful with the
44 * art_svp_merge: Merge the segments of two svp's.
45 * @svp1: One svp to merge.
46 * @svp2: The other svp to merge.
48 * Merges the segments of two SVP's into a new one. The resulting
49 * #ArtSVP data structure will share the segments of the argument
50 * svp's, so it is probably a good idea to free it shallowly,
51 * especially if the arguments will be freed with art_svp_free().
53 * Return value: The merged #ArtSVP.
56 art_svp_merge (const ArtSVP *svp1, const ArtSVP *svp2)
62 svp_new = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
63 (svp1->n_segs + svp2->n_segs - 1) *
67 for (ix = 0; ix < svp1->n_segs + svp2->n_segs; ix++)
69 if (ix1 < svp1->n_segs &&
70 (ix2 == svp2->n_segs ||
71 art_svp_seg_compare (&svp1->segs[ix1], &svp2->segs[ix2]) < 1))
72 svp_new->segs[ix] = svp1->segs[ix1++];
74 svp_new->segs[ix] = svp2->segs[ix2++];
87 print_ps_vpath (ArtVpath *vpath)
91 printf ("gsave %d %d translate 1 -1 scale\n", XOFF, YOFF);
92 for (i = 0; vpath[i].code != ART_END; i++)
94 switch (vpath[i].code)
97 printf ("%g %g moveto\n", vpath[i].x, vpath[i].y);
100 printf ("%g %g lineto\n", vpath[i].x, vpath[i].y);
106 printf ("stroke grestore showpage\n");
112 print_ps_svp (ArtSVP *vpath)
116 printf ("%% begin\n");
117 for (i = 0; i < vpath->n_segs; i++)
119 printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
120 for (j = 0; j < vpath->segs[i].n_points; j++)
122 printf ("%g %g %s\n",
123 XOFF + vpath->segs[i].points[j].x,
124 YOFF - vpath->segs[i].points[j].y,
125 j ? "lineto" : "moveto");
127 printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
128 XOFF + vpath->segs[i].points[0].x - DELT,
129 YOFF - DELT - vpath->segs[i].points[0].y,
130 XOFF + vpath->segs[i].points[0].x - DELT,
131 YOFF - vpath->segs[i].points[0].y,
132 XOFF + vpath->segs[i].points[0].x + DELT,
133 YOFF - vpath->segs[i].points[0].y,
134 XOFF + vpath->segs[i].points[0].x + DELT,
135 YOFF - DELT - vpath->segs[i].points[0].y);
136 printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
137 XOFF + vpath->segs[i].points[j - 1].x - DELT,
138 YOFF + DELT - vpath->segs[i].points[j - 1].y,
139 XOFF + vpath->segs[i].points[j - 1].x - DELT,
140 YOFF - vpath->segs[i].points[j - 1].y,
141 XOFF + vpath->segs[i].points[j - 1].x + DELT,
142 YOFF - vpath->segs[i].points[j - 1].y,
143 XOFF + vpath->segs[i].points[j - 1].x + DELT,
144 YOFF + DELT - vpath->segs[i].points[j - 1].y);
148 printf ("showpage\n");
152 #ifndef ART_USE_NEW_INTERSECTOR
154 art_svp_merge_perturbed (const ArtSVP *svp1, const ArtSVP *svp2)
156 ArtVpath *vpath1, *vpath2;
157 ArtVpath *vpath1_p, *vpath2_p;
158 ArtSVP *svp1_p, *svp2_p;
161 vpath1 = art_vpath_from_svp (svp1);
162 vpath1_p = art_vpath_perturb (vpath1);
164 svp1_p = art_svp_from_vpath (vpath1_p);
167 vpath2 = art_vpath_from_svp (svp2);
168 vpath2_p = art_vpath_perturb (vpath2);
170 svp2_p = art_svp_from_vpath (vpath2_p);
173 svp_new = art_svp_merge (svp1_p, svp2_p);
175 print_ps_svp (svp1_p);
176 print_ps_svp (svp2_p);
177 print_ps_svp (svp_new);
186 /* Compute the union of two vector paths.
188 Status of this routine:
190 Basic correctness: Seems to work.
192 Numerical stability: We cheat (adding random perturbation). Thus,
193 it seems very likely that no numerical stability problems will be
196 Speed: Would be better if we didn't go to unsorted vector path
197 and back to add the perturbation.
199 Precision: The perturbation fuzzes the coordinates slightly. In
200 cases of butting segments, razor thin long holes may appear.
204 * art_svp_union: Compute the union of two sorted vector paths.
205 * @svp1: One sorted vector path.
206 * @svp2: The other sorted vector path.
208 * Computes the union of the two argument svp's. Given two svp's with
209 * winding numbers of 0 and 1 everywhere, the resulting winding number
210 * will be 1 where either (or both) of the argument svp's has a
211 * winding number 1, 0 otherwise. The result is newly allocated.
213 * Currently, this routine has accuracy problems pending the
214 * implementation of the new intersector.
216 * Return value: The union of @svp1 and @svp2.
219 art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2)
221 #ifdef ART_USE_NEW_INTERSECTOR
222 ArtSVP *svp3, *svp_new;
225 svp3 = art_svp_merge (svp1, svp2);
226 swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
227 art_svp_intersector (svp3, swr);
228 svp_new = art_svp_writer_rewind_reap (swr);
229 art_free (svp3); /* shallow free because svp3 contains shared segments */
233 ArtSVP *svp3, *svp4, *svp_new;
235 svp3 = art_svp_merge_perturbed (svp1, svp2);
236 svp4 = art_svp_uncross (svp3);
239 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_POSITIVE);
242 print_ps_svp (svp_new);
249 /* Compute the intersection of two vector paths.
251 Status of this routine:
253 Basic correctness: Seems to work.
255 Numerical stability: We cheat (adding random perturbation). Thus,
256 it seems very likely that no numerical stability problems will be
259 Speed: Would be better if we didn't go to unsorted vector path
260 and back to add the perturbation.
262 Precision: The perturbation fuzzes the coordinates slightly. In
263 cases of butting segments, razor thin long isolated segments may
269 * art_svp_intersect: Compute the intersection of two sorted vector paths.
270 * @svp1: One sorted vector path.
271 * @svp2: The other sorted vector path.
273 * Computes the intersection of the two argument svp's. Given two
274 * svp's with winding numbers of 0 and 1 everywhere, the resulting
275 * winding number will be 1 where both of the argument svp's has a
276 * winding number 1, 0 otherwise. The result is newly allocated.
278 * Currently, this routine has accuracy problems pending the
279 * implementation of the new intersector.
281 * Return value: The intersection of @svp1 and @svp2.
284 art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2)
286 #ifdef ART_USE_NEW_INTERSECTOR
287 ArtSVP *svp3, *svp_new;
290 svp3 = art_svp_merge (svp1, svp2);
291 swr = art_svp_writer_rewind_new (ART_WIND_RULE_INTERSECT);
292 art_svp_intersector (svp3, swr);
293 svp_new = art_svp_writer_rewind_reap (swr);
294 art_free (svp3); /* shallow free because svp3 contains shared segments */
298 ArtSVP *svp3, *svp4, *svp_new;
300 svp3 = art_svp_merge_perturbed (svp1, svp2);
301 svp4 = art_svp_uncross (svp3);
304 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_INTERSECT);
310 /* Compute the symmetric difference of two vector paths.
312 Status of this routine:
314 Basic correctness: Seems to work.
316 Numerical stability: We cheat (adding random perturbation). Thus,
317 it seems very likely that no numerical stability problems will be
320 Speed: We could do a lot better by scanning through the svp
321 representations and culling out any segments that are exactly
322 identical. It would also be better if we didn't go to unsorted
323 vector path and back to add the perturbation.
325 Precision: Awful. In the case of inputs which are similar (the
326 common case for canvas display), the entire outline is "hairy." In
327 addition, the perturbation fuzzes the coordinates slightly. It can
328 be used as a conservative approximation.
333 * art_svp_diff: Compute the symmetric difference of two sorted vector paths.
334 * @svp1: One sorted vector path.
335 * @svp2: The other sorted vector path.
337 * Computes the symmetric of the two argument svp's. Given two svp's
338 * with winding numbers of 0 and 1 everywhere, the resulting winding
339 * number will be 1 where either, but not both, of the argument svp's
340 * has a winding number 1, 0 otherwise. The result is newly allocated.
342 * Currently, this routine has accuracy problems pending the
343 * implementation of the new intersector.
345 * Return value: The symmetric difference of @svp1 and @svp2.
348 art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2)
350 #ifdef ART_USE_NEW_INTERSECTOR
351 ArtSVP *svp3, *svp_new;
354 svp3 = art_svp_merge (svp1, svp2);
355 swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN);
356 art_svp_intersector (svp3, swr);
357 svp_new = art_svp_writer_rewind_reap (swr);
358 art_free (svp3); /* shallow free because svp3 contains shared segments */
362 ArtSVP *svp3, *svp4, *svp_new;
364 svp3 = art_svp_merge_perturbed (svp1, svp2);
365 svp4 = art_svp_uncross (svp3);
368 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_ODDEVEN);
374 #ifdef ART_USE_NEW_INTERSECTOR
376 art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2)
379 ArtSVP *svp3, *svp_new;
383 svp2_mod = (ArtSVP *) svp2; /* get rid of the const for a while */
385 /* First invert svp2 to "turn it inside out" */
386 for (i = 0; i < svp2_mod->n_segs; i++)
387 svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
389 svp3 = art_svp_merge (svp1, svp2_mod);
390 swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
391 art_svp_intersector (svp3, swr);
392 svp_new = art_svp_writer_rewind_reap (swr);
393 art_free (svp3); /* shallow free because svp3 contains shared segments */
395 /* Flip svp2 back to its original state */
396 for (i = 0; i < svp2_mod->n_segs; i++)
397 svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
401 #endif /* ART_USE_NEW_INTERSECTOR */