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"
32 static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line, char fill)
39 /* factor which determines into how many line fragments a spline is converted */
40 double subfraction = 2.4;//0.3
44 if(l2->type == gfx_moveTo) {
46 } if(l2->type == gfx_lineTo) {
48 } if(l2->type == gfx_splineTo) {
49 int parts = (int)(sqrt(fabs(l2->x-2*l2->sx+x) + fabs(l2->y-2*l2->sy+y))*subfraction);
60 vec = art_new (ArtVpath, len+1);
65 if(l2->type == gfx_moveTo) {
66 vec[pos].code = ART_MOVETO;
71 } else if(l2->type == gfx_lineTo) {
72 vec[pos].code = ART_LINETO;
77 } else if(l2->type == gfx_splineTo) {
79 int parts = (int)(sqrt(fabs(l2->x-2*l2->sx+x) + fabs(l2->y-2*l2->sy+y))*subfraction);
80 double stepsize = parts?1.0/parts:0;
81 for(i=0;i<=parts;i++) {
82 double t = (double)i*stepsize;
83 vec[pos].code = ART_LINETO;
84 vec[pos].x = l2->x*t*t + 2*l2->sx*t*(1-t) + x*(1-t)*(1-t);
85 vec[pos].y = l2->y*t*t + 2*l2->sy*t*(1-t) + y*(1-t)*(1-t);
94 vec[pos].code = ART_END;
97 /* Fix "dotted" lines. Those are lines where singular points are created
98 by a moveto x,y lineto x,y combination. We "fix" these by shifting the
99 point in the lineto a little bit to the right
100 These should only occur in strokes, not in fills, so do this only
101 when we know we're not filling.
104 for(t=0;vec[t].code!=ART_END;t++) {
105 if(t>0 && vec[t-1].code==ART_MOVETO && vec[t].code==ART_LINETO
106 && vec[t+1].code!=ART_LINETO
107 && vec[t-1].x == vec[t].x
108 && vec[t-1].y == vec[t].y) {
116 /* Find adjacent identical points. If an ajdacent pair of identical
117 points is found, the moveto is removed (unless both are movetos).
118 So moveto x,y lineto x,y becomes lineto x,y
119 lineto x,y lineto x,y becomes lineto x,y
120 lineto x,y moveto x,y becomes lineto x,y
121 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 one
129 int type = ART_MOVETO;
130 if(vec[t-1].code==ART_LINETO || vec[t].code==ART_LINETO)
132 memcpy(&vec[t], &vec[t+1], sizeof(vec[0]) * (pos - t));
140 /* adjacency remover disabled for now, pending code inspection */
143 // Check for further non-adjacent identical points. We don't want any
144 // points other than the first and last points to exactly match.
146 // If we do find matching points, move the second point slightly. This
147 // currently moves the duplicate 2% towards the midpoint of its neighbours
148 // (easier to calculate than 2% down the perpendicular to the line joining
149 // the neighbours) but limiting the change to 0.1 twips to avoid visual
150 // problems when the shapes are large. Note that there is no minimum
151 // change: if the neighbouring points are colinear and equally spaced,
152 // e.g. they were generated as part of a straight spline, then the
153 // duplicate point may not actually move.
155 // The scan for duplicates algorithm is quadratic in the number of points:
156 // there's probably a better method but the numbers of points is generally
157 // small so this will do for now.
160 for(; i < (pos - 1); ++i)
162 for (j = 0; j < i; ++j)
164 if ((vec[i].x == vec[j].x)
165 && (vec[i].y == vec[j].y))
167 // points match; shuffle point
168 double dx = (vec[i-1].x + vec[i+1].x - (vec[i].x * 2.0)) / 100.0;
169 double dy = (vec[i-1].y + vec[i+1].y - (vec[i].y * 2.0)) / 100.0;
170 double dxxyy = (dx*dx) + (dy*dy);
173 // This is more than 0.1 twip's distance; scale down
174 double dscale = sqrt(dxxyy) * 10.0;
189 void show_path(ArtSVP*path)
192 printf("Segments: %d\n", path->n_segs);
193 for(t=0;t<path->n_segs;t++) {
194 ArtSVPSeg* seg = &path->segs[t];
195 printf("Segment %d: %d points, %s, BBox: (%f,%f,%f,%f)\n",
196 t, seg->n_points, seg->dir==0?"UP ":"DOWN",
197 seg->bbox.x0, seg->bbox.y0, seg->bbox.x1, seg->bbox.y1);
199 for(p=0;p<seg->n_points;p++) {
200 ArtPoint* point = &seg->points[p];
201 printf(" (%f,%f)\n", point->x, point->y);
207 ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb)
209 ArtVpath* vec = gfxline_to_ArtVpath(line, 1);
211 ArtVpath* vec2 = art_vpath_perturb(vec);
215 ArtSVP *svp = art_svp_from_vpath(vec);
218 // We need to make sure that the SVP we now have bounds an area (i.e. the
219 // source line wound anticlockwise) rather than excludes an area (i.e. the
220 // line wound clockwise). It seems that PDF (or xpdf) is less strict about
221 // this for bitmaps than it is for fill areas.
223 // To check this, we'll sum the cross products of all pairs of adjacent
224 // lines. If the result is positive, the direction is correct; if not, we
225 // need to reverse the sense of the SVP generated. The easiest way to do
226 // this is to flip the up/down flags of all the segments.
228 // This is approximate; since the gfxline_t structure can contain any
229 // combination of moveTo, lineTo and splineTo in any order, not all pairs
230 // of lines in the shape that share a point need be described next to each
231 // other in the sequence. For ease, we'll consider only pairs of lines
232 // stored as lineTos and splineTos without intervening moveTos.
234 // TODO is this a valid algorithm? My vector maths is rusty.
236 // It may be more correct to instead reverse the line before we feed it
237 // into gfxfilltoSVP. However, this seems equivalent and is easier to
239 double total_cross_product = 0.0;
240 gfxline_t* cursor = line;
243 double x_last = cursor->x;
244 double y_last = cursor->y;
245 cursor = cursor->next;
247 while((cursor != NULL) && (cursor->next != NULL))
249 if (((cursor->type == gfx_lineTo) || (cursor->type == gfx_splineTo))
250 && ((cursor->next->type == gfx_lineTo) || (cursor->next->type == gfx_splineTo)))
252 // Process these lines
254 // In this space (x right, y down) the cross-product is
255 // (x1 * y0) - (x0 * y1)
256 double x0 = cursor->x - x_last;
257 double y0 = cursor->y - y_last;
258 double x1 = cursor->next->x - cursor->x;
259 double y1 = cursor->next->y - cursor->y;
260 total_cross_product += (x1 * y0) - (x0 * y1);
265 cursor = cursor->next;
268 if (total_cross_product < 0.0)
271 for(; i < svp->n_segs; ++i)
273 if (svp->segs[i].dir != 0)
274 svp->segs[i].dir = 0;
276 svp->segs[i].dir = 1;
282 gfxline_t* gfxpoly_to_gfxline(gfxpoly_t*poly)
284 ArtSVP*svp = (ArtSVP*)poly;
288 for(t=0;t<svp->n_segs;t++) {
289 size += svp->segs[t].n_points + 1;
291 gfxline_t* lines = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)*size);
293 for(t=0;t<svp->n_segs;t++) {
294 ArtSVPSeg* seg = &svp->segs[t];
296 for(p=0;p<seg->n_points;p++) {
297 lines[pos].type = p==0?gfx_moveTo:gfx_lineTo;
298 ArtPoint* point = &seg->points[p];
299 lines[pos].x = point->x;
300 lines[pos].y = point->y;
301 lines[pos].next = &lines[pos+1];
306 lines[pos-1].next = 0;
313 gfxpoly_t* gfxpoly_fillToPoly(gfxline_t*line)
315 ArtSVP* svp = gfxfillToSVP(line, 1);
316 if (svp->n_segs > 500)
319 gfxline_t* lineCursor = line;
320 while(lineCursor != NULL)
322 if(lineCursor->type != gfx_moveTo) ++lineParts;
323 lineCursor = lineCursor->next;
325 fprintf(stderr, "arts_fill abandonning shape with %d segments (%d line parts)\n", svp->n_segs, lineParts);
327 return (gfxpoly_t*)gfxpoly_strokeToPoly(0, 0, gfx_capButt, gfx_joinMiter, 0);
329 ArtSVP* svp2 = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_ODDEVEN);
331 return (gfxpoly_t*)svp;
334 gfxpoly_t* gfxpoly_intersect(gfxpoly_t*poly1, gfxpoly_t*poly2)
336 ArtSVP* svp1 = (ArtSVP*)poly1;
337 ArtSVP* svp2 = (ArtSVP*)poly2;
339 ArtSVP* svp = art_svp_intersect(svp1, svp2);
340 return (gfxpoly_t*)svp;
343 gfxpoly_t* gfxpoly_union(gfxpoly_t*poly1, gfxpoly_t*poly2)
345 ArtSVP* svp1 = (ArtSVP*)poly1;
346 ArtSVP* svp2 = (ArtSVP*)poly2;
348 ArtSVP* svp = art_svp_union(svp1, svp2);
349 return (gfxpoly_t*)svp;
352 gfxpoly_t* gfxpoly_strokeToPoly(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, double miterLimit)
354 ArtVpath* vec = gfxline_to_ArtVpath(line, 0);
356 ArtSVP *svp = art_svp_vpath_stroke (vec,
357 (joint_style==gfx_joinMiter)?ART_PATH_STROKE_JOIN_MITER:
358 ((joint_style==gfx_joinRound)?ART_PATH_STROKE_JOIN_ROUND:
359 ((joint_style==gfx_joinBevel)?ART_PATH_STROKE_JOIN_BEVEL:ART_PATH_STROKE_JOIN_BEVEL)),
360 (cap_style==gfx_capButt)?ART_PATH_STROKE_CAP_BUTT:
361 ((cap_style==gfx_capRound)?ART_PATH_STROKE_CAP_ROUND:
362 ((cap_style==gfx_capSquare)?ART_PATH_STROKE_CAP_SQUARE:ART_PATH_STROKE_CAP_SQUARE)),
364 miterLimit, //miter_limit
368 return (gfxpoly_t*)svp;
371 gfxline_t* gfxline_circularToEvenOdd(gfxline_t*line)
373 ArtSVP* svp = gfxfillToSVP(line, 1);
374 ArtSVP* svp2 = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_POSITIVE);
375 gfxline_t* result = gfxpoly_to_gfxline((gfxpoly_t*)svp2);
381 gfxpoly_t* gfxpoly_createbox(double x1, double y1,double x2, double y2)
383 ArtVpath *vec = art_new (ArtVpath, 5+1);
384 vec[0].code = ART_MOVETO;
387 vec[1].code = ART_LINETO;
390 vec[2].code = ART_LINETO;
393 vec[3].code = ART_LINETO;
396 vec[4].code = ART_LINETO;
399 vec[5].code = ART_END;
402 ArtSVP *svp = art_svp_from_vpath(vec);
404 return (gfxpoly_t*)svp;
407 void gfxpoly_free(gfxpoly_t*poly)
409 ArtSVP*svp = (ArtSVP*)poly;