1 /* Libart_LGPL - library of basic graphic primitives
2 * Copyright (C) 1998-2000 Raph Levien
3 *
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.
8 *
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.
13 *
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.
18 */
20 #define noVERBOSE
22 /* Vector path set operations, over sorted vpaths. */
24 #include "art_misc.h"
26 #include "art_svp.h"
27 #include "art_vpath.h"
28 #include "art_svp_vpath.h"
29 #include "art_svp.h"
30 #ifdef ART_USE_NEW_INTERSECTOR
31 #include "art_svp_intersect.h"
32 #else
33 #include "art_svp_wind.h"
34 #endif
35 #include "art_svp_ops.h"
36 #include "art_vpath_svp.h"
38 /* Merge the segments of the two svp's. The resulting svp will share
39 segments with args passed in, so be super-careful with the
40 allocation. */
41 /**
42 * art_svp_merge: Merge the segments of two svp's.
43 * @svp1: One svp to merge.
44 * @svp2: The other svp to merge.
45 *
46 * Merges the segments of two SVP's into a new one. The resulting
47 * #ArtSVP data structure will share the segments of the argument
48 * svp's, so it is probably a good idea to free it shallowly,
49 * especially if the arguments will be freed with art_svp_free().
50 *
51 * Return value: The merged #ArtSVP.
52 **/
53 static ArtSVP *
54 art_svp_merge (const ArtSVP *svp1, const ArtSVP *svp2)
55 {
56 ArtSVP *svp_new;
57 int ix;
58 int ix1, ix2;
60 svp_new = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
61 (svp1->n_segs + svp2->n_segs - 1) *
62 sizeof(ArtSVPSeg));
63 ix1 = 0;
64 ix2 = 0;
65 for (ix = 0; ix < svp1->n_segs + svp2->n_segs; ix++)
66 {
67 if (ix1 < svp1->n_segs &&
68 (ix2 == svp2->n_segs ||
69 art_svp_seg_compare (&svp1->segs[ix1], &svp2->segs[ix2]) < 1))
70 svp_new->segs[ix] = svp1->segs[ix1++];
71 else
72 svp_new->segs[ix] = svp2->segs[ix2++];
73 }
75 svp_new->n_segs = ix;
76 return svp_new;
77 }
79 #ifdef VERBOSE
81 #define XOFF 50
82 #define YOFF 700
84 static void
85 print_ps_vpath (ArtVpath *vpath)
86 {
87 int i;
89 for (i = 0; vpath[i].code != ART_END; i++)
90 {
91 switch (vpath[i].code)
92 {
93 case ART_MOVETO:
94 printf ("%g %g moveto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
95 break;
96 case ART_LINETO:
97 printf ("%g %g lineto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
98 break;
99 default:
100 break;
101 }
102 }
103 printf ("stroke showpage\n");
104 }
106 #define DELT 4
108 static void
109 print_ps_svp (ArtSVP *vpath)
110 {
111 int i, j;
113 printf ("%% begin\n");
114 for (i = 0; i < vpath->n_segs; i++)
115 {
116 printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
117 for (j = 0; j < vpath->segs[i].n_points; j++)
118 {
119 printf ("%g %g %s\n",
120 XOFF + vpath->segs[i].points[j].x,
121 YOFF - vpath->segs[i].points[j].y,
122 j ? "lineto" : "moveto");
123 }
124 printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
125 XOFF + vpath->segs[i].points[0].x - DELT,
126 YOFF - DELT - vpath->segs[i].points[0].y,
127 XOFF + vpath->segs[i].points[0].x - DELT,
128 YOFF - vpath->segs[i].points[0].y,
129 XOFF + vpath->segs[i].points[0].x + DELT,
130 YOFF - vpath->segs[i].points[0].y,
131 XOFF + vpath->segs[i].points[0].x + DELT,
132 YOFF - DELT - vpath->segs[i].points[0].y);
133 printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
134 XOFF + vpath->segs[i].points[j - 1].x - DELT,
135 YOFF + DELT - vpath->segs[i].points[j - 1].y,
136 XOFF + vpath->segs[i].points[j - 1].x - DELT,
137 YOFF - vpath->segs[i].points[j - 1].y,
138 XOFF + vpath->segs[i].points[j - 1].x + DELT,
139 YOFF - vpath->segs[i].points[j - 1].y,
140 XOFF + vpath->segs[i].points[j - 1].x + DELT,
141 YOFF + DELT - vpath->segs[i].points[j - 1].y);
142 printf ("stroke\n");
143 }
145 printf ("showpage\n");
146 }
147 #endif
149 #ifndef ART_USE_NEW_INTERSECTOR
150 static ArtSVP *
151 art_svp_merge_perturbed (const ArtSVP *svp1, const ArtSVP *svp2)
152 {
153 ArtVpath *vpath1, *vpath2;
154 ArtVpath *vpath1_p, *vpath2_p;
155 ArtSVP *svp1_p, *svp2_p;
156 ArtSVP *svp_new;
158 vpath1 = art_vpath_from_svp (svp1);
159 vpath1_p = art_vpath_perturb (vpath1);
160 art_free (vpath1);
161 svp1_p = art_svp_from_vpath (vpath1_p);
162 art_free (vpath1_p);
164 vpath2 = art_vpath_from_svp (svp2);
165 vpath2_p = art_vpath_perturb (vpath2);
166 art_free (vpath2);
167 svp2_p = art_svp_from_vpath (vpath2_p);
168 art_free (vpath2_p);
170 svp_new = art_svp_merge (svp1_p, svp2_p);
171 #ifdef VERBOSE
172 print_ps_svp (svp1_p);
173 print_ps_svp (svp2_p);
174 print_ps_svp (svp_new);
175 #endif
176 art_free (svp1_p);
177 art_free (svp2_p);
179 return svp_new;
180 }
181 #endif
183 /* Compute the union of two vector paths.
185 Status of this routine:
187 Basic correctness: Seems to work.
189 Numerical stability: We cheat (adding random perturbation). Thus,
190 it seems very likely that no numerical stability problems will be
191 seen in practice.
193 Speed: Would be better if we didn't go to unsorted vector path
194 and back to add the perturbation.
196 Precision: The perturbation fuzzes the coordinates slightly. In
197 cases of butting segments, razor thin long holes may appear.
199 */
200 /**
201 * art_svp_union: Compute the union of two sorted vector paths.
202 * @svp1: One sorted vector path.
203 * @svp2: The other sorted vector path.
204 *
205 * Computes the union of the two argument svp's. Given two svp's with
206 * winding numbers of 0 and 1 everywhere, the resulting winding number
207 * will be 1 where either (or both) of the argument svp's has a
208 * winding number 1, 0 otherwise. The result is newly allocated.
209 *
210 * Currently, this routine has accuracy problems pending the
211 * implementation of the new intersector.
212 *
213 * Return value: The union of @svp1 and @svp2.
214 **/
215 ArtSVP *
216 art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2)
217 {
218 #ifdef ART_USE_NEW_INTERSECTOR
219 ArtSVP *svp3, *svp_new;
220 ArtSvpWriter *swr;
222 svp3 = art_svp_merge (svp1, svp2);
223 swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
224 art_svp_intersector (svp3, swr);
225 svp_new = art_svp_writer_rewind_reap (swr);
226 art_free (svp3); /* shallow free because svp3 contains shared segments */
228 return svp_new;
229 #else
230 ArtSVP *svp3, *svp4, *svp_new;
232 svp3 = art_svp_merge_perturbed (svp1, svp2);
233 svp4 = art_svp_uncross (svp3);
234 art_svp_free (svp3);
236 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_POSITIVE);
237 #ifdef VERBOSE
238 print_ps_svp (svp4);
239 print_ps_svp (svp_new);
240 #endif
241 art_svp_free (svp4);
242 return svp_new;
243 #endif
244 }
246 /* Compute the intersection of two vector paths.
248 Status of this routine:
250 Basic correctness: Seems to work.
252 Numerical stability: We cheat (adding random perturbation). Thus,
253 it seems very likely that no numerical stability problems will be
254 seen in practice.
256 Speed: Would be better if we didn't go to unsorted vector path
257 and back to add the perturbation.
259 Precision: The perturbation fuzzes the coordinates slightly. In
260 cases of butting segments, razor thin long isolated segments may
261 appear.
263 */
265 /**
266 * art_svp_intersect: Compute the intersection of two sorted vector paths.
267 * @svp1: One sorted vector path.
268 * @svp2: The other sorted vector path.
269 *
270 * Computes the intersection of the two argument svp's. Given two
271 * svp's with winding numbers of 0 and 1 everywhere, the resulting
272 * winding number will be 1 where both of the argument svp's has a
273 * winding number 1, 0 otherwise. The result is newly allocated.
274 *
275 * Currently, this routine has accuracy problems pending the
276 * implementation of the new intersector.
277 *
278 * Return value: The intersection of @svp1 and @svp2.
279 **/
280 ArtSVP *
281 art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2)
282 {
283 #ifdef ART_USE_NEW_INTERSECTOR
284 ArtSVP *svp3, *svp_new;
285 ArtSvpWriter *swr;
287 svp3 = art_svp_merge (svp1, svp2);
288 swr = art_svp_writer_rewind_new (ART_WIND_RULE_INTERSECT);
289 art_svp_intersector (svp3, swr);
290 svp_new = art_svp_writer_rewind_reap (swr);
291 art_free (svp3); /* shallow free because svp3 contains shared segments */
293 return svp_new;
294 #else
295 ArtSVP *svp3, *svp4, *svp_new;
297 svp3 = art_svp_merge_perturbed (svp1, svp2);
298 svp4 = art_svp_uncross (svp3);
299 art_svp_free (svp3);
301 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_INTERSECT);
302 art_svp_free (svp4);
303 return svp_new;
304 #endif
305 }
307 /* Compute the symmetric difference of two vector paths.
309 Status of this routine:
311 Basic correctness: Seems to work.
313 Numerical stability: We cheat (adding random perturbation). Thus,
314 it seems very likely that no numerical stability problems will be
315 seen in practice.
317 Speed: We could do a lot better by scanning through the svp
318 representations and culling out any segments that are exactly
319 identical. It would also be better if we didn't go to unsorted
320 vector path and back to add the perturbation.
322 Precision: Awful. In the case of inputs which are similar (the
323 common case for canvas display), the entire outline is "hairy." In
324 addition, the perturbation fuzzes the coordinates slightly. It can
325 be used as a conservative approximation.
327 */
329 /**
330 * art_svp_diff: Compute the symmetric difference of two sorted vector paths.
331 * @svp1: One sorted vector path.
332 * @svp2: The other sorted vector path.
333 *
334 * Computes the symmetric of the two argument svp's. Given two svp's
335 * with winding numbers of 0 and 1 everywhere, the resulting winding
336 * number will be 1 where either, but not both, of the argument svp's
337 * has a winding number 1, 0 otherwise. The result is newly allocated.
338 *
339 * Currently, this routine has accuracy problems pending the
340 * implementation of the new intersector.
341 *
342 * Return value: The symmetric difference of @svp1 and @svp2.
343 **/
344 ArtSVP *
345 art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2)
346 {
347 #ifdef ART_USE_NEW_INTERSECTOR
348 ArtSVP *svp3, *svp_new;
349 ArtSvpWriter *swr;
351 svp3 = art_svp_merge (svp1, svp2);
352 swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN);
353 art_svp_intersector (svp3, swr);
354 svp_new = art_svp_writer_rewind_reap (swr);
355 art_free (svp3); /* shallow free because svp3 contains shared segments */
357 return svp_new;
358 #else
359 ArtSVP *svp3, *svp4, *svp_new;
361 svp3 = art_svp_merge_perturbed (svp1, svp2);
362 svp4 = art_svp_uncross (svp3);
363 art_svp_free (svp3);
365 svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_ODDEVEN);
366 art_svp_free (svp4);
367 return svp_new;
368 #endif
369 }
371 /* todo: implement minus */