new memberinfo field subfunctions
[swftools.git] / lib / art / art_svp_ops.c
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  */
19
20 #define noVERBOSE
21
22 /* Vector path set operations, over sorted vpaths. */
23
24 #include "config.h"
25 #include "art_svp_ops.h"
26
27 #include "art_misc.h"
28
29 #include "art_svp.h"
30 #include "art_vpath.h"
31 #include "art_svp_vpath.h"
32 #include "art_svp.h"
33 #ifdef ART_USE_NEW_INTERSECTOR
34 #include "art_svp_intersect.h"
35 #else
36 #include "art_svp_wind.h"
37 #endif
38 #include "art_vpath_svp.h"
39
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
42    allocation.  */
43 /**
44  * art_svp_merge: Merge the segments of two svp's.
45  * @svp1: One svp to merge.
46  * @svp2: The other svp to merge.
47  *
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().
52  *
53  * Return value: The merged #ArtSVP.
54  **/
55 ArtSVP * art_svp_merge (const ArtSVP *svp1, const ArtSVP *svp2)
56 {
57   ArtSVP *svp_new;
58   int ix;
59   int ix1, ix2;
60
61   svp_new = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
62                                  (svp1->n_segs + svp2->n_segs - 1) *
63                                  sizeof(ArtSVPSeg));
64   ix1 = 0;
65   ix2 = 0;
66   for (ix = 0; ix < svp1->n_segs + svp2->n_segs; ix++)
67     {
68       if (ix1 < svp1->n_segs &&
69           (ix2 == svp2->n_segs ||
70            art_svp_seg_compare (&svp1->segs[ix1], &svp2->segs[ix2]) < 1))
71         svp_new->segs[ix] = svp1->segs[ix1++];
72       else
73         svp_new->segs[ix] = svp2->segs[ix2++];
74     }
75
76   svp_new->n_segs = ix;
77   return svp_new;
78 }
79
80 #ifdef VERBOSE
81
82 #define XOFF 50
83 #define YOFF 700
84
85 static void
86 print_ps_vpath (ArtVpath *vpath)
87 {
88   int i;
89
90   printf ("gsave %d %d translate 1 -1 scale\n", XOFF, YOFF);
91   for (i = 0; vpath[i].code != ART_END; i++)
92     {
93       switch (vpath[i].code)
94         {
95         case ART_MOVETO:
96           printf ("%g %g moveto\n", vpath[i].x, vpath[i].y);
97           break;
98         case ART_LINETO:
99           printf ("%g %g lineto\n", vpath[i].x, vpath[i].y);
100           break;
101         default:
102           break;
103         }
104     }
105   printf ("stroke grestore showpage\n");
106 }
107
108 #define DELT 4
109
110 static void
111 print_ps_svp (ArtSVP *vpath)
112 {
113   int i, j;
114
115   printf ("%% begin\n");
116   for (i = 0; i < vpath->n_segs; i++)
117     {
118       printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
119       for (j = 0; j < vpath->segs[i].n_points; j++)
120         {
121           printf ("%g %g %s\n",
122                   XOFF + vpath->segs[i].points[j].x,
123                   YOFF - vpath->segs[i].points[j].y,
124                   j ? "lineto" : "moveto");
125         }
126       printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
127               XOFF + vpath->segs[i].points[0].x - DELT,
128               YOFF - DELT - 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 - vpath->segs[i].points[0].y,
133               XOFF + vpath->segs[i].points[0].x + DELT,
134               YOFF - DELT - vpath->segs[i].points[0].y);
135       printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
136               XOFF + vpath->segs[i].points[j - 1].x - DELT,
137               YOFF + DELT - 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 - vpath->segs[i].points[j - 1].y,
142               XOFF + vpath->segs[i].points[j - 1].x + DELT,
143               YOFF + DELT - vpath->segs[i].points[j - 1].y);
144       printf ("stroke\n");
145     }
146
147   printf ("showpage\n");
148 }
149 #endif
150
151 #ifndef ART_USE_NEW_INTERSECTOR
152 static ArtSVP *
153 art_svp_merge_perturbed (const ArtSVP *svp1, const ArtSVP *svp2)
154 {
155   ArtVpath *vpath1, *vpath2;
156   ArtVpath *vpath1_p, *vpath2_p;
157   ArtSVP *svp1_p, *svp2_p;
158   ArtSVP *svp_new;
159
160   vpath1 = art_vpath_from_svp (svp1);
161   vpath1_p = art_vpath_perturb (vpath1);
162   art_free (vpath1);
163   svp1_p = art_svp_from_vpath (vpath1_p);
164   art_free (vpath1_p);
165
166   vpath2 = art_vpath_from_svp (svp2);
167   vpath2_p = art_vpath_perturb (vpath2);
168   art_free (vpath2);
169   svp2_p = art_svp_from_vpath (vpath2_p);
170   art_free (vpath2_p);
171
172   svp_new = art_svp_merge (svp1_p, svp2_p);
173 #ifdef VERBOSE
174   print_ps_svp (svp1_p);
175   print_ps_svp (svp2_p);
176   print_ps_svp (svp_new);
177 #endif
178   art_free (svp1_p);
179   art_free (svp2_p);
180
181   return svp_new;
182 }
183 #endif
184
185 /* Compute the union of two vector paths.
186
187    Status of this routine:
188
189    Basic correctness: Seems to work.
190
191    Numerical stability: We cheat (adding random perturbation). Thus,
192    it seems very likely that no numerical stability problems will be
193    seen in practice.
194
195    Speed: Would be better if we didn't go to unsorted vector path
196    and back to add the perturbation.
197
198    Precision: The perturbation fuzzes the coordinates slightly. In
199    cases of butting segments, razor thin long holes may appear.
200
201 */
202 /**
203  * art_svp_union: Compute the union of two sorted vector paths.
204  * @svp1: One sorted vector path.
205  * @svp2: The other sorted vector path.
206  *
207  * Computes the union of the two argument svp's. Given two svp's with
208  * winding numbers of 0 and 1 everywhere, the resulting winding number
209  * will be 1 where either (or both) of the argument svp's has a
210  * winding number 1, 0 otherwise. The result is newly allocated.
211  *
212  * Currently, this routine has accuracy problems pending the
213  * implementation of the new intersector.
214  *
215  * Return value: The union of @svp1 and @svp2.
216  **/
217 ArtSVP *
218 art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2)
219 {
220 #ifdef ART_USE_NEW_INTERSECTOR 
221   ArtSVP *svp3, *svp_new;
222   ArtSvpWriter *swr;
223
224   svp3 = art_svp_merge (svp1, svp2);
225   swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
226   art_svp_intersector (svp3, swr);
227   svp_new = art_svp_writer_rewind_reap (swr);
228   art_free (svp3); /* shallow free because svp3 contains shared segments */
229
230   return svp_new;
231 #else
232   ArtSVP *svp3, *svp4, *svp_new;
233
234   svp3 = art_svp_merge_perturbed (svp1, svp2);
235   svp4 = art_svp_uncross (svp3);
236   art_svp_free (svp3);
237
238   svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_POSITIVE);
239 #ifdef VERBOSE
240   print_ps_svp (svp4);
241   print_ps_svp (svp_new);
242 #endif
243   art_svp_free (svp4);
244   return svp_new;
245 #endif
246 }
247
248 /* Compute the intersection of two vector paths.
249
250    Status of this routine:
251
252    Basic correctness: Seems to work.
253
254    Numerical stability: We cheat (adding random perturbation). Thus,
255    it seems very likely that no numerical stability problems will be
256    seen in practice.
257
258    Speed: Would be better if we didn't go to unsorted vector path
259    and back to add the perturbation.
260
261    Precision: The perturbation fuzzes the coordinates slightly. In
262    cases of butting segments, razor thin long isolated segments may
263    appear.
264
265 */
266
267 /**
268  * art_svp_intersect: Compute the intersection of two sorted vector paths.
269  * @svp1: One sorted vector path.
270  * @svp2: The other sorted vector path.
271  *
272  * Computes the intersection of the two argument svp's. Given two
273  * svp's with winding numbers of 0 and 1 everywhere, the resulting
274  * winding number will be 1 where both of the argument svp's has a
275  * winding number 1, 0 otherwise. The result is newly allocated.
276  *
277  * Currently, this routine has accuracy problems pending the
278  * implementation of the new intersector.
279  *
280  * Return value: The intersection of @svp1 and @svp2.
281  **/
282 ArtSVP *
283 art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2)
284 {
285 #ifdef ART_USE_NEW_INTERSECTOR 
286   ArtSVP *svp3, *svp_new;
287   ArtSvpWriter *swr;
288
289   svp3 = art_svp_merge (svp1, svp2);
290   swr = art_svp_writer_rewind_new (ART_WIND_RULE_INTERSECT);
291   art_svp_intersector (svp3, swr);
292   svp_new = art_svp_writer_rewind_reap (swr);
293   art_free (svp3); /* shallow free because svp3 contains shared segments */
294
295   return svp_new;
296 #else
297   ArtSVP *svp3, *svp4, *svp_new;
298
299   svp3 = art_svp_merge_perturbed (svp1, svp2);
300   svp4 = art_svp_uncross (svp3);
301   art_svp_free (svp3);
302
303   svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_INTERSECT);
304   art_svp_free (svp4);
305   return svp_new;
306 #endif
307 }
308
309 /* Compute the symmetric difference of two vector paths.
310
311    Status of this routine:
312
313    Basic correctness: Seems to work.
314
315    Numerical stability: We cheat (adding random perturbation). Thus,
316    it seems very likely that no numerical stability problems will be
317    seen in practice.
318
319    Speed: We could do a lot better by scanning through the svp
320    representations and culling out any segments that are exactly
321    identical. It would also be better if we didn't go to unsorted
322    vector path and back to add the perturbation.
323
324    Precision: Awful. In the case of inputs which are similar (the
325    common case for canvas display), the entire outline is "hairy." In
326    addition, the perturbation fuzzes the coordinates slightly. It can
327    be used as a conservative approximation.
328
329 */
330
331 /**
332  * art_svp_diff: Compute the symmetric difference of two sorted vector paths.
333  * @svp1: One sorted vector path.
334  * @svp2: The other sorted vector path.
335  *
336  * Computes the symmetric of the two argument svp's. Given two svp's
337  * with winding numbers of 0 and 1 everywhere, the resulting winding
338  * number will be 1 where either, but not both, of the argument svp's
339  * has a winding number 1, 0 otherwise. The result is newly allocated.
340  *
341  * Currently, this routine has accuracy problems pending the
342  * implementation of the new intersector.
343  *
344  * Return value: The symmetric difference of @svp1 and @svp2.
345  **/
346 ArtSVP *
347 art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2)
348 {
349 #ifdef ART_USE_NEW_INTERSECTOR 
350   ArtSVP *svp3, *svp_new;
351   ArtSvpWriter *swr;
352
353   svp3 = art_svp_merge (svp1, svp2);
354   swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN);
355   art_svp_intersector (svp3, swr);
356   svp_new = art_svp_writer_rewind_reap (swr);
357   art_free (svp3); /* shallow free because svp3 contains shared segments */
358
359   return svp_new;
360 #else
361   ArtSVP *svp3, *svp4, *svp_new;
362
363   svp3 = art_svp_merge_perturbed (svp1, svp2);
364   svp4 = art_svp_uncross (svp3);
365   art_svp_free (svp3);
366
367   svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_ODDEVEN);
368   art_svp_free (svp4);
369   return svp_new;
370 #endif
371 }
372
373 #ifdef ART_USE_NEW_INTERSECTOR 
374 ArtSVP *
375 art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2)
376 {
377   ArtSVP *svp2_mod;
378   ArtSVP *svp3, *svp_new;
379   ArtSvpWriter *swr;
380   int i;
381
382   svp2_mod = (ArtSVP *) svp2; /* get rid of the const for a while */
383
384   /* First invert svp2 to "turn it inside out" */
385   for (i = 0; i < svp2_mod->n_segs; i++)
386     svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
387
388   svp3 = art_svp_merge (svp1, svp2_mod);
389   swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
390   art_svp_intersector (svp3, swr);
391   svp_new = art_svp_writer_rewind_reap (swr);
392   art_free (svp3); /* shallow free because svp3 contains shared segments */
393
394   /* Flip svp2 back to its original state */
395   for (i = 0; i < svp2_mod->n_segs; i++)
396     svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
397
398   return svp_new;
399 }
400 #endif /* ART_USE_NEW_INTERSECTOR */