3 Various boolean polygon functions.
5 Part of the swftools package.
7 Copyright (c) 2005 Matthias Kramm <kramm@quiss.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
23 #include "../config.h"
24 #include "gfxdevice.h"
27 #include "art/libart.h"
28 #include "art/art_svp_intersect.h"
33 static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line, char fill)
40 /* factor which determines into how many line fragments a spline is converted */
41 double subfraction = 2.4;//0.3
45 if(l2->type == gfx_moveTo) {
47 } if(l2->type == gfx_lineTo) {
49 } if(l2->type == gfx_splineTo) {
50 int parts = (int)(sqrt(fabs(l2->x-2*l2->sx+x) + fabs(l2->y-2*l2->sy+y))*subfraction);
61 vec = art_new (ArtVpath, len+1);
66 if(l2->type == gfx_moveTo) {
67 vec[pos].code = ART_MOVETO;
72 } else if(l2->type == gfx_lineTo) {
73 vec[pos].code = ART_LINETO;
78 } else if(l2->type == gfx_splineTo) {
80 int parts = (int)(sqrt(fabs(l2->x-2*l2->sx+x) + fabs(l2->y-2*l2->sy+y))*subfraction);
81 double stepsize = parts?1.0/parts:0;
82 for(i=0;i<=parts;i++) {
83 double t = (double)i*stepsize;
84 vec[pos].code = ART_LINETO;
85 vec[pos].x = l2->x*t*t + 2*l2->sx*t*(1-t) + x*(1-t)*(1-t);
86 vec[pos].y = l2->y*t*t + 2*l2->sy*t*(1-t) + y*(1-t)*(1-t);
95 vec[pos].code = ART_END;
98 /* Fix "dotted" lines. Those are lines where singular points are created
99 by a moveto x,y lineto x,y combination. We "fix" these by shifting the
100 point in the lineto a little bit to the right
101 These should only occur in strokes, not in fills, so do this only
102 when we know we're not filling.
105 for(t=0;vec[t].code!=ART_END;t++) {
106 if(t>0 && vec[t-1].code==ART_MOVETO && vec[t].code==ART_LINETO
107 && vec[t+1].code!=ART_LINETO
108 && vec[t-1].x == vec[t].x
109 && vec[t-1].y == vec[t].y) {
117 /* Find adjacent identical points. If an ajdacent pair of identical
118 points is found, the second is removed.
119 So moveto x,y lineto x,y becomes moveto x,y
120 lineto x,y lineto x,y becomes lineto x,y
121 lineto x,y moveto x,y becomes lineto x,y
122 moveto x,y moveto x,y becomes moveto x,y
127 if ((vec[t-1].x == vec[t].x) && (vec[t-1].y == vec[t].y)) {
128 // adjacent identical points; remove the second
129 memcpy(&vec[t], &vec[t+1], sizeof(vec[0]) * (pos - t - 1));
136 /* adjacency remover disabled for now, pending code inspection */
139 // Check for further non-adjacent identical points. We don't want any
140 // points other than the first and last points to exactly match.
142 // If we do find matching points, move the second point slightly. This
143 // currently moves the duplicate 2% towards the midpoint of its neighbours
144 // (easier to calculate than 2% down the perpendicular to the line joining
145 // the neighbours) but limiting the change to 0.1 twips to avoid visual
146 // problems when the shapes are large. Note that there is no minimum
147 // change: if the neighbouring points are colinear and equally spaced,
148 // e.g. they were generated as part of a straight spline, then the
149 // duplicate point may not actually move.
151 // The scan for duplicates algorithm is quadratic in the number of points:
152 // there's probably a better method but the numbers of points is generally
153 // small so this will do for now.
156 for(; i < (pos - 1); ++i)
158 for (j = 0; j < i; ++j)
160 if ((vec[i].x == vec[j].x)
161 && (vec[i].y == vec[j].y))
163 // points match; shuffle point
164 double dx = (vec[i-1].x + vec[i+1].x - (vec[i].x * 2.0)) / 100.0;
165 double dy = (vec[i-1].y + vec[i+1].y - (vec[i].y * 2.0)) / 100.0;
166 double dxxyy = (dx*dx) + (dy*dy);
169 // This is more than 0.1 twip's distance; scale down
170 double dscale = sqrt(dxxyy) * 10.0;
185 void show_path(ArtSVP*path)
188 printf("Segments: %d\n", path->n_segs);
189 for(t=0;t<path->n_segs;t++) {
190 ArtSVPSeg* seg = &path->segs[t];
191 printf("Segment %d: %d points, %s, BBox: (%f,%f,%f,%f)\n",
192 t, seg->n_points, seg->dir==0?"UP ":"DOWN",
193 seg->bbox.x0, seg->bbox.y0, seg->bbox.x1, seg->bbox.y1);
195 for(p=0;p<seg->n_points;p++) {
196 ArtPoint* point = &seg->points[p];
197 printf(" (%f,%f)\n", point->x, point->y);
203 ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb)
205 ArtVpath* vec = gfxline_to_ArtVpath(line, 1);
207 ArtVpath* vec2 = art_vpath_perturb(vec);
211 ArtSVP *svp = art_svp_from_vpath(vec);
214 // We need to make sure that the SVP we now have bounds an area (i.e. the
215 // source line wound anticlockwise) rather than excludes an area (i.e. the
216 // line wound clockwise). It seems that PDF (or xpdf) is less strict about
217 // this for bitmaps than it is for fill areas.
219 // To check this, we'll sum the cross products of all pairs of adjacent
220 // lines. If the result is positive, the direction is correct; if not, we
221 // need to reverse the sense of the SVP generated. The easiest way to do
222 // this is to flip the up/down flags of all the segments.
224 // This is approximate; since the gfxline_t structure can contain any
225 // combination of moveTo, lineTo and splineTo in any order, not all pairs
226 // of lines in the shape that share a point need be described next to each
227 // other in the sequence. For ease, we'll consider only pairs of lines
228 // stored as lineTos and splineTos without intervening moveTos.
230 // TODO is this a valid algorithm? My vector maths is rusty.
232 // It may be more correct to instead reverse the line before we feed it
233 // into gfxfilltoSVP. However, this seems equivalent and is easier to
235 double total_cross_product = 0.0;
236 gfxline_t* cursor = line;
239 double x_last = cursor->x;
240 double y_last = cursor->y;
241 cursor = cursor->next;
243 while((cursor != NULL) && (cursor->next != NULL))
245 if (((cursor->type == gfx_lineTo) || (cursor->type == gfx_splineTo))
246 && ((cursor->next->type == gfx_lineTo) || (cursor->next->type == gfx_splineTo)))
248 // Process these lines
250 // In this space (x right, y down) the cross-product is
251 // (x1 * y0) - (x0 * y1)
252 double x0 = cursor->x - x_last;
253 double y0 = cursor->y - y_last;
254 double x1 = cursor->next->x - cursor->x;
255 double y1 = cursor->next->y - cursor->y;
256 total_cross_product += (x1 * y0) - (x0 * y1);
261 cursor = cursor->next;
264 if (total_cross_product < 0.0)
267 for(; i < svp->n_segs; ++i)
269 if (svp->segs[i].dir != 0)
270 svp->segs[i].dir = 0;
272 svp->segs[i].dir = 1;
278 gfxline_t* gfxpoly_to_gfxline(gfxpoly_t*poly)
280 ArtSVP*svp = (ArtSVP*)poly;
284 for(t=0;t<svp->n_segs;t++) {
285 size += svp->segs[t].n_points + 1;
287 gfxline_t* lines = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)*size);
289 for(t=0;t<svp->n_segs;t++) {
290 ArtSVPSeg* seg = &svp->segs[t];
292 for(p=0;p<seg->n_points;p++) {
293 lines[pos].type = p==0?gfx_moveTo:gfx_lineTo;
294 ArtPoint* point = &seg->points[p];
295 lines[pos].x = point->x;
296 lines[pos].y = point->y;
297 lines[pos].next = &lines[pos+1];
302 lines[pos-1].next = 0;
309 gfxpoly_t* gfxpoly_fillToPoly(gfxline_t*line)
311 ArtSVP* svp = gfxfillToSVP(line, 1);
312 if (svp->n_segs > 500)
315 gfxline_t* lineCursor = line;
316 while(lineCursor != NULL)
318 if(lineCursor->type != gfx_moveTo) ++lineParts;
319 lineCursor = lineCursor->next;
321 fprintf(stderr, "arts_fill abandonning shape with %d segments (%d line parts)\n", svp->n_segs, lineParts);
324 /* return empty shape */
325 return (gfxpoly_t*)gfxpoly_strokeToPoly(0, 0, gfx_capButt, gfx_joinMiter, 0);
328 /* for some reason, we need to rewind / self-intersect the polygons that gfxfillToSVP
329 returns- art probably uses a different fill mode (circular?) for vpaths */
330 ArtSVP*svp_uncrossed=0;
331 #ifdef ART_USE_NEW_INTERSECTOR
332 ArtSvpWriter * swr = art_svp_writer_rewind_new(ART_WIND_RULE_ODDEVEN);
333 art_svp_intersector(svp, swr);
334 svp_uncrossed = art_svp_writer_rewind_reap(swr);
336 svp_uncrossed = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_ODDEVEN);
341 return (gfxpoly_t*)svp;
344 gfxpoly_t* gfxpoly_intersect(gfxpoly_t*poly1, gfxpoly_t*poly2)
346 ArtSVP* svp1 = (ArtSVP*)poly1;
347 ArtSVP* svp2 = (ArtSVP*)poly2;
349 ArtSVP* svp = art_svp_intersect(svp1, svp2);
350 return (gfxpoly_t*)svp;
353 gfxpoly_t* gfxpoly_union(gfxpoly_t*poly1, gfxpoly_t*poly2)
355 ArtSVP* svp1 = (ArtSVP*)poly1;
356 ArtSVP* svp2 = (ArtSVP*)poly2;
358 ArtSVP* svp = art_svp_union(svp1, svp2);
359 return (gfxpoly_t*)svp;
362 gfxpoly_t* gfxpoly_strokeToPoly(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, double miterLimit)
364 ArtVpath* vec = gfxline_to_ArtVpath(line, 0);
365 ArtSVP *svp = art_svp_vpath_stroke (vec,
366 (joint_style==gfx_joinMiter)?ART_PATH_STROKE_JOIN_MITER:
367 ((joint_style==gfx_joinRound)?ART_PATH_STROKE_JOIN_ROUND:
368 ((joint_style==gfx_joinBevel)?ART_PATH_STROKE_JOIN_BEVEL:ART_PATH_STROKE_JOIN_BEVEL)),
369 (cap_style==gfx_capButt)?ART_PATH_STROKE_CAP_BUTT:
370 ((cap_style==gfx_capRound)?ART_PATH_STROKE_CAP_ROUND:
371 ((cap_style==gfx_capSquare)?ART_PATH_STROKE_CAP_SQUARE:ART_PATH_STROKE_CAP_SQUARE)),
373 miterLimit, //miter_limit
377 return (gfxpoly_t*)svp;
380 gfxline_t* gfxline_circularToEvenOdd(gfxline_t*line)
382 ArtSVP* svp = gfxfillToSVP(line, 1);
384 ArtSVP* svp_rewinded;
385 #ifdef ART_USE_NEW_INTERSECTOR
386 ArtSvpWriter * swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
387 art_svp_intersector(svp, swr);
388 svp_rewinded = art_svp_writer_rewind_reap(swr);
391 svp_rewinded = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_POSITIVE);
394 gfxline_t* result = gfxpoly_to_gfxline((gfxpoly_t*)svp_rewinded);
396 art_svp_free(svp_rewinded);
400 gfxpoly_t* gfxpoly_createbox(double x1, double y1,double x2, double y2)
402 ArtVpath *vec = art_new (ArtVpath, 5+1);
403 vec[0].code = ART_MOVETO;
406 vec[1].code = ART_LINETO;
409 vec[2].code = ART_LINETO;
412 vec[3].code = ART_LINETO;
415 vec[4].code = ART_LINETO;
418 vec[5].code = ART_END;
421 ArtSVP *svp = art_svp_from_vpath(vec);
423 return (gfxpoly_t*)svp;
426 void gfxpoly_free(gfxpoly_t*poly)
428 ArtSVP*svp = (ArtSVP*)poly;