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
126 if ((vec[t-1].x == vec[t].x) && (vec[t-1].y == vec[t].y)) {
127 // adjacent identical points; remove one
128 int type = ART_MOVETO;
129 if(vec[t-1].code==ART_LINETO || vec[t].code==ART_LINETO)
131 memcpy(&vec[t], &vec[t+1], sizeof(vec[0]) * (pos - t));
139 /* adjacency remover disabled for now, pending code inspection */
142 // Check for further non-adjacent identical points. We don't want any
143 // points other than the first and last points to exactly match.
145 // If we do find matching points, move the second point slightly. This
146 // currently moves the duplicate 2% towards the midpoint of its neighbours
147 // (easier to calculate than 2% down the perpendicular to the line joining
148 // the neighbours) but limiting the change to 0.1 twips to avoid visual
149 // problems when the shapes are large. Note that there is no minimum
150 // change: if the neighbouring points are colinear and equally spaced,
151 // e.g. they were generated as part of a straight spline, then the
152 // duplicate point may not actually move.
154 // The scan for duplicates algorithm is quadratic in the number of points:
155 // there's probably a better method but the numbers of points is generally
156 // small so this will do for now.
159 for(; i < (pos - 1); ++i)
161 for (j = 0; j < i; ++j)
163 if ((vec[i].x == vec[j].x)
164 && (vec[i].y == vec[j].y))
166 // points match; shuffle point
167 double dx = (vec[i-1].x + vec[i+1].x - (vec[i].x * 2.0)) / 100.0;
168 double dy = (vec[i-1].y + vec[i+1].y - (vec[i].y * 2.0)) / 100.0;
169 double dxxyy = (dx*dx) + (dy*dy);
172 // This is more than 0.1 twip's distance; scale down
173 double dscale = sqrt(dxxyy) * 10.0;
188 void show_path(ArtSVP*path)
191 printf("Segments: %d\n", path->n_segs);
192 for(t=0;t<path->n_segs;t++) {
193 ArtSVPSeg* seg = &path->segs[t];
194 printf("Segment %d: %d points, %s, BBox: (%f,%f,%f,%f)\n",
195 t, seg->n_points, seg->dir==0?"UP ":"DOWN",
196 seg->bbox.x0, seg->bbox.y0, seg->bbox.x1, seg->bbox.y1);
198 for(p=0;p<seg->n_points;p++) {
199 ArtPoint* point = &seg->points[p];
200 printf(" (%f,%f)\n", point->x, point->y);
206 ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb)
208 ArtVpath* vec = gfxline_to_ArtVpath(line, 1);
210 ArtVpath* vec2 = art_vpath_perturb(vec);
214 ArtSVP *svp = art_svp_from_vpath(vec);
217 // We need to make sure that the SVP we now have bounds an area (i.e. the
218 // source line wound anticlockwise) rather than excludes an area (i.e. the
219 // line wound clockwise). It seems that PDF (or xpdf) is less strict about
220 // this for bitmaps than it is for fill areas.
222 // To check this, we'll sum the cross products of all pairs of adjacent
223 // lines. If the result is positive, the direction is correct; if not, we
224 // need to reverse the sense of the SVP generated. The easiest way to do
225 // this is to flip the up/down flags of all the segments.
227 // This is approximate; since the gfxline_t structure can contain any
228 // combination of moveTo, lineTo and splineTo in any order, not all pairs
229 // of lines in the shape that share a point need be described next to each
230 // other in the sequence. For ease, we'll consider only pairs of lines
231 // stored as lineTos and splineTos without intervening moveTos.
233 // TODO is this a valid algorithm? My vector maths is rusty.
235 // It may be more correct to instead reverse the line before we feed it
236 // into gfxfilltoSVP. However, this seems equivalent and is easier to
238 double total_cross_product = 0.0;
239 gfxline_t* cursor = line;
242 double x_last = cursor->x;
243 double y_last = cursor->y;
244 cursor = cursor->next;
246 while((cursor != NULL) && (cursor->next != NULL))
248 if (((cursor->type == gfx_lineTo) || (cursor->type == gfx_splineTo))
249 && ((cursor->next->type == gfx_lineTo) || (cursor->next->type == gfx_splineTo)))
251 // Process these lines
253 // In this space (x right, y down) the cross-product is
254 // (x1 * y0) - (x0 * y1)
255 double x0 = cursor->x - x_last;
256 double y0 = cursor->y - y_last;
257 double x1 = cursor->next->x - cursor->x;
258 double y1 = cursor->next->y - cursor->y;
259 total_cross_product += (x1 * y0) - (x0 * y1);
264 cursor = cursor->next;
267 if (total_cross_product < 0.0)
270 for(; i < svp->n_segs; ++i)
272 if (svp->segs[i].dir != 0)
273 svp->segs[i].dir = 0;
275 svp->segs[i].dir = 1;
281 gfxline_t* gfxpoly_to_gfxline(gfxpoly_t*poly)
283 ArtSVP*svp = (ArtSVP*)poly;
287 for(t=0;t<svp->n_segs;t++) {
288 size += svp->segs[t].n_points + 1;
290 gfxline_t* lines = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)*size);
292 for(t=0;t<svp->n_segs;t++) {
293 ArtSVPSeg* seg = &svp->segs[t];
295 for(p=0;p<seg->n_points;p++) {
296 lines[pos].type = p==0?gfx_moveTo:gfx_lineTo;
297 ArtPoint* point = &seg->points[p];
298 lines[pos].x = point->x;
299 lines[pos].y = point->y;
300 lines[pos].next = &lines[pos+1];
305 lines[pos-1].next = 0;
312 gfxpoly_t* gfxpoly_fillToPoly(gfxline_t*line)
314 ArtSVP* svp = gfxfillToSVP(line, 1);
315 if (svp->n_segs > 500)
318 gfxline_t* lineCursor = line;
319 while(lineCursor != NULL)
321 if(lineCursor->type != gfx_moveTo) ++lineParts;
322 lineCursor = lineCursor->next;
324 fprintf(stderr, "arts_fill abandonning shape with %d segments (%d line parts)\n", svp->n_segs, lineParts);
326 return (gfxpoly_t*)gfxpoly_strokeToPoly(0, 0, gfx_capButt, gfx_joinMiter, 0);
328 ArtSVP* svp2 = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_ODDEVEN);
329 art_svp_free(svp);svp=svp2;
330 return (gfxpoly_t*)svp;
333 gfxpoly_t* gfxpoly_intersect(gfxpoly_t*poly1, gfxpoly_t*poly2)
335 ArtSVP* svp1 = (ArtSVP*)poly1;
336 ArtSVP* svp2 = (ArtSVP*)poly2;
338 ArtSVP* svp = art_svp_intersect(svp1, svp2);
339 return (gfxpoly_t*)svp;
342 gfxpoly_t* gfxpoly_union(gfxpoly_t*poly1, gfxpoly_t*poly2)
344 ArtSVP* svp1 = (ArtSVP*)poly1;
345 ArtSVP* svp2 = (ArtSVP*)poly2;
347 ArtSVP* svp = art_svp_union(svp1, svp2);
348 return (gfxpoly_t*)svp;
351 gfxpoly_t* gfxpoly_strokeToPoly(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, double miterLimit)
353 ArtVpath* vec = gfxline_to_ArtVpath(line, 0);
354 ArtSVP *svp = art_svp_vpath_stroke (vec,
355 (joint_style==gfx_joinMiter)?ART_PATH_STROKE_JOIN_MITER:
356 ((joint_style==gfx_joinRound)?ART_PATH_STROKE_JOIN_ROUND:
357 ((joint_style==gfx_joinBevel)?ART_PATH_STROKE_JOIN_BEVEL:ART_PATH_STROKE_JOIN_BEVEL)),
358 (cap_style==gfx_capButt)?ART_PATH_STROKE_CAP_BUTT:
359 ((cap_style==gfx_capRound)?ART_PATH_STROKE_CAP_ROUND:
360 ((cap_style==gfx_capSquare)?ART_PATH_STROKE_CAP_SQUARE:ART_PATH_STROKE_CAP_SQUARE)),
362 miterLimit, //miter_limit
366 return (gfxpoly_t*)svp;
369 gfxline_t* gfxline_circularToEvenOdd(gfxline_t*line)
371 ArtSVP* svp = gfxfillToSVP(line, 1);
372 ArtSVP* svp2 = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_POSITIVE);
373 gfxline_t* result = gfxpoly_to_gfxline((gfxpoly_t*)svp2);
379 gfxpoly_t* gfxpoly_createbox(double x1, double y1,double x2, double y2)
381 ArtVpath *vec = art_new (ArtVpath, 5+1);
382 vec[0].code = ART_MOVETO;
385 vec[1].code = ART_LINETO;
388 vec[2].code = ART_LINETO;
391 vec[3].code = ART_LINETO;
394 vec[4].code = ART_LINETO;
397 vec[5].code = ART_END;
400 ArtSVP *svp = art_svp_from_vpath(vec);
402 return (gfxpoly_t*)svp;
405 void gfxpoly_free(gfxpoly_t*poly)
407 ArtSVP*svp = (ArtSVP*)poly;