added shape simplification and more log messages
[swftools.git] / lib / gfxpoly.c
index 2e2b855..4dd27a7 100644 (file)
 #include "gfxdevice.h"
 #include "gfxtools.h"
 #include "gfxpoly.h"
 #include "gfxdevice.h"
 #include "gfxtools.h"
 #include "gfxpoly.h"
+#include "mem.h"
 #include "art/libart.h"
 #include "art/libart.h"
+#include "art/art_svp_intersect.h"
+#include "log.h"
 #include <assert.h>
 #include <memory.h>
 #include <math.h>
 #include <assert.h>
 #include <memory.h>
 #include <math.h>
@@ -103,9 +106,9 @@ static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line, char fill)
        int t;
        for(t=0;vec[t].code!=ART_END;t++) {
            if(t>0 && vec[t-1].code==ART_MOVETO && vec[t].code==ART_LINETO 
        int t;
        for(t=0;vec[t].code!=ART_END;t++) {
            if(t>0 && vec[t-1].code==ART_MOVETO && vec[t].code==ART_LINETO 
-                   && vec[t+1].code!=ART_LINETO
-               && vec[t-1].x == vec[t].x
-               && vec[t-1].y == vec[t].y) {
+                   && vec[t+1].code!=ART_LINETO &&
+               vec[t-1].x == vec[t].x && 
+                vec[t-1].y == vec[t].y) {
                vec[t].x += 0.01;
            }
            x = vec[t].x;
                vec[t].x += 0.01;
            }
            x = vec[t].x;
@@ -114,22 +117,29 @@ static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line, char fill)
     }
 
     /* Find adjacent identical points. If an ajdacent pair of identical
     }
 
     /* Find adjacent identical points. If an ajdacent pair of identical
-       points is found, the moveto is removed (unless both are movetos).
-       So moveto x,y lineto x,y becomes lineto x,y
+       points is found, the second is removed.
+       So moveto x,y lineto x,y becomes moveto x,y
           lineto x,y lineto x,y becomes lineto x,y
           lineto x,y moveto x,y becomes lineto x,y
           moveto x,y moveto x,y becomes moveto x,y
           lineto x,y lineto x,y becomes lineto x,y
           lineto x,y moveto x,y becomes lineto x,y
           moveto x,y moveto x,y becomes moveto x,y
+          lineto x,y lineto x2,y2 becomes lineto x2,y2 (if dir(x,y) ~= dir(x2,y2))
      */
     int t = 1;
     while(t < pos)
     {
      */
     int t = 1;
     while(t < pos)
     {
-       if ((vec[t-1].x == vec[t].x) && (vec[t-1].y == vec[t].y)) {
-           // adjacent identical points; remove one
-           int type = ART_MOVETO;
-           if(vec[t-1].code==ART_LINETO || vec[t].code==ART_LINETO)
-               type = ART_LINETO;
-           memcpy(&vec[t], &vec[t+1], sizeof(vec[0]) * (pos - t));
-           vec[t].code = type;
+        double dx = vec[t].x-vec[t-1].x;
+        double dy = vec[t].y-vec[t-1].y;
+        char samedir = 0;
+        if(t<pos-1 && vec[t].code == ART_LINETO && vec[t+1].code == ART_LINETO) {
+            double dx2 = vec[t+1].x-vec[t].x;
+            double dy2 = vec[t+1].y-vec[t].y;
+            if(fabs(dx*dy2 - dy*dx2) < 0.001) {
+                samedir=1;
+            }
+        }
+       if(fabs(dx) + fabs(dy) < 0.01 || samedir) {
+           // adjacent identical points; remove the second
+           memcpy(&vec[t], &vec[t+1], sizeof(vec[0]) * (pos - t - 1));
            pos--;
        } else {
            t++;
            pos--;
        } else {
            t++;
@@ -203,17 +213,114 @@ void show_path(ArtSVP*path)
     printf("\n");
 }
 
     printf("\n");
 }
 
-ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb)
+void write_svp_postscript(const char*filename, ArtSVP*svp)
+{
+    FILE*fi = fopen(filename, "wb");
+    int i, j;
+    double xmin=0,ymin=0,xmax=0,ymax=0;
+    fprintf(fi, "%% begin\n");
+    for (i = 0; i < svp->n_segs; i++) {
+        for (j = 0; j < svp->segs[i].n_points; j++) {
+            double x = svp->segs[i].points[j].x;
+            double y = svp->segs[i].points[j].y;
+            if(i==0 && j==0) {
+                xmin = xmax = x;
+                ymin = ymax = y;
+            } else {
+                if(x < xmin) xmin = x;
+                if(x > xmax) xmax = x;
+                if(y < ymin) ymin = y;
+                if(y > ymax) ymax = y;
+            }
+        }
+    }
+    if(xmax == xmin) xmax=xmin+1;
+    if(ymax == ymin) ymax=ymin+1;
+
+    for (i = 0; i < svp->n_segs; i++)
+      {
+        fprintf(fi, "%g setgray\n", svp->segs[i].dir ? 0.7 : 0);
+        for (j = 0; j < svp->segs[i].n_points; j++)
+          {
+            fprintf(fi, "%g %g %s\n",
+                    20 + 550*(svp->segs[i].points[j].x-xmin)/(xmax-xmin),
+                    820 - 800*(svp->segs[i].points[j].y-ymin)/(ymax-ymin),
+                    j ? "lineto" : "moveto");
+          }
+        fprintf(fi, "stroke\n");
+      }
+
+    fprintf(fi, "showpage\n");
+    fclose(fi);
+}
+
+void write_vpath_postscript(const char*filename, ArtVpath*path)
+{
+    FILE*fi = fopen(filename, "wb");
+    int i, j;
+    double xmin=0,ymin=0,xmax=0,ymax=0;
+    fprintf(fi, "%% begin\n");
+    ArtVpath*p = path;
+    char first = 1;
+    while(p->code != ART_END) {
+        if(p->code == ART_MOVETO || p->code == ART_MOVETO_OPEN) {
+            if(!first)
+                fprintf(fi, "stroke\n");
+            first = 0;
+            fprintf(fi, "1 setgray\n");
+            fprintf(fi, "%.32f %.32f moveto\n", p->x, p->y);
+        } else {
+            fprintf(fi, "%.32f %.32f lineto\n", p->x, p->y);
+        }
+        p++;
+    }
+    if(!first)
+        fprintf(fi, "stroke\n");
+    fprintf(fi, "showpage\n");
+    fclose(fi);
+}
+
+static int vpath_len(ArtVpath*svp)
+{
+    int len = 0;
+    while(svp->code != ART_END) {
+        svp ++;
+        len ++;
+    }
+    return len;
+}
+
+int gfxline_len(gfxline_t*line)
+{
+    gfxline_t*i = line;
+    int len = 0;
+    while(i) {
+        len ++;
+        i = i->next;
+    }
+    return len;
+}
+
+
+static int debug = 0;
+static ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb)
 {
     ArtVpath* vec = gfxline_to_ArtVpath(line, 1);
 {
     ArtVpath* vec = gfxline_to_ArtVpath(line, 1);
+    msg("<verbose> Casting gfxline of %d segments (%d line segments) to a gfxpoly", gfxline_len(line), vpath_len(vec));
+
     if(perturb) {
        ArtVpath* vec2 = art_vpath_perturb(vec);
        free(vec);
        vec = vec2;
     }
     ArtSVP *svp = art_svp_from_vpath(vec);
     if(perturb) {
        ArtVpath* vec2 = art_vpath_perturb(vec);
        free(vec);
        vec = vec2;
     }
     ArtSVP *svp = art_svp_from_vpath(vec);
+    if(debug) {
+        write_vpath_postscript("vpath.ps", vec);
+        write_svp_postscript("polygon.ps", svp);
+    }
     free(vec);
 
     free(vec);
 
+#if 0
     // We need to make sure that the SVP we now have bounds an area (i.e. the
     // source line wound anticlockwise) rather than excludes an area (i.e. the
     // line wound clockwise). It seems that PDF (or xpdf) is less strict about
     // We need to make sure that the SVP we now have bounds an area (i.e. the
     // source line wound anticlockwise) rather than excludes an area (i.e. the
     // line wound clockwise). It seems that PDF (or xpdf) is less strict about
@@ -275,6 +382,8 @@ ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb)
                svp->segs[i].dir = 1;
        }
     }
                svp->segs[i].dir = 1;
        }
     }
+#endif
+
     return svp;
 }
 
     return svp;
 }
 
@@ -284,6 +393,9 @@ gfxline_t* gfxpoly_to_gfxline(gfxpoly_t*poly)
     int size = 0;
     int t;
     int pos = 0;
     int size = 0;
     int t;
     int pos = 0;
+
+    msg("<verbose> Casting polygon of %d segments back to gfxline", svp->n_segs);
+
     for(t=0;t<svp->n_segs;t++) {
        size += svp->segs[t].n_points + 1;
     }
     for(t=0;t<svp->n_segs;t++) {
        size += svp->segs[t].n_points + 1;
     }
@@ -308,25 +420,32 @@ gfxline_t* gfxpoly_to_gfxline(gfxpoly_t*poly)
        return 0;
     }
 }
        return 0;
     }
 }
-
 gfxpoly_t* gfxpoly_fillToPoly(gfxline_t*line)
 {
     ArtSVP* svp = gfxfillToSVP(line, 1);
 gfxpoly_t* gfxpoly_fillToPoly(gfxline_t*line)
 {
     ArtSVP* svp = gfxfillToSVP(line, 1);
-    if (svp->n_segs > 500)
-    {
-       int lineParts = 0;
-       gfxline_t* lineCursor = line;
-       while(lineCursor != NULL)
-       {
-           if(lineCursor->type != gfx_moveTo) ++lineParts;
-           lineCursor = lineCursor->next;
-       }
-       fprintf(stderr, "arts_fill abandonning shape with %d segments (%d line parts)\n", svp->n_segs, lineParts);
-       art_svp_free(svp);
-       return (gfxpoly_t*)gfxpoly_strokeToPoly(0, 0, gfx_capButt, gfx_joinMiter, 0);
+
+    /* we do xor-filling by default, so dir is always 1 
+       (actually for oddeven rewinding it makes no difference, but
+        it's "cleaner")
+     */
+    int t;
+    for(t=0; t<svp->n_segs; t++) {
+        svp->segs[t].dir = 1;
     }
     }
-    ArtSVP* svp2 = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_ODDEVEN);
-    art_svp_free(svp);svp=svp2;
+
+    /* for some reason, we need to rewind / self-intersect the polygons that gfxfillToSVP
+       returns- art probably uses a different fill mode (circular?) for vpaths */
+    ArtSVP*svp_uncrossed=0;
+#ifdef ART_USE_NEW_INTERSECTOR
+    ArtSvpWriter * swr = art_svp_writer_rewind_new(ART_WIND_RULE_ODDEVEN);
+    art_svp_intersector(svp, swr);
+    svp_uncrossed = art_svp_writer_rewind_reap(swr);
+#else
+    svp_uncrossed = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_ODDEVEN);
+#endif
+    art_svp_free(svp);
+    svp=svp_uncrossed;
+
     return (gfxpoly_t*)svp;
 }
 
     return (gfxpoly_t*)svp;
 }
 
@@ -334,6 +453,7 @@ gfxpoly_t* gfxpoly_intersect(gfxpoly_t*poly1, gfxpoly_t*poly2)
 {
     ArtSVP* svp1 = (ArtSVP*)poly1;
     ArtSVP* svp2 = (ArtSVP*)poly2;
 {
     ArtSVP* svp1 = (ArtSVP*)poly1;
     ArtSVP* svp2 = (ArtSVP*)poly2;
+    msg("<verbose> Intersecting two polygons of %d and %d segments", svp1->n_segs, svp2->n_segs);
        
     ArtSVP* svp = art_svp_intersect(svp1, svp2);
     return (gfxpoly_t*)svp;
        
     ArtSVP* svp = art_svp_intersect(svp1, svp2);
     return (gfxpoly_t*)svp;
@@ -343,6 +463,7 @@ gfxpoly_t* gfxpoly_union(gfxpoly_t*poly1, gfxpoly_t*poly2)
 {
     ArtSVP* svp1 = (ArtSVP*)poly1;
     ArtSVP* svp2 = (ArtSVP*)poly2;
 {
     ArtSVP* svp1 = (ArtSVP*)poly1;
     ArtSVP* svp2 = (ArtSVP*)poly2;
+    msg("<verbose> Unifying two polygons of %d and %d segments", svp1->n_segs, svp2->n_segs);
        
     ArtSVP* svp = art_svp_union(svp1, svp2);
     return (gfxpoly_t*)svp;
        
     ArtSVP* svp = art_svp_union(svp1, svp2);
     return (gfxpoly_t*)svp;
@@ -351,6 +472,8 @@ gfxpoly_t* gfxpoly_union(gfxpoly_t*poly1, gfxpoly_t*poly2)
 gfxpoly_t* gfxpoly_strokeToPoly(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, double miterLimit)
 {
     ArtVpath* vec = gfxline_to_ArtVpath(line, 0);
 gfxpoly_t* gfxpoly_strokeToPoly(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, double miterLimit)
 {
     ArtVpath* vec = gfxline_to_ArtVpath(line, 0);
+    msg("<verbose> Casting gfxline of %d segments to a stroke-polygon", gfxline_len(line));
+
     ArtSVP *svp = art_svp_vpath_stroke (vec,
                        (joint_style==gfx_joinMiter)?ART_PATH_STROKE_JOIN_MITER:
                        ((joint_style==gfx_joinRound)?ART_PATH_STROKE_JOIN_ROUND:
     ArtSVP *svp = art_svp_vpath_stroke (vec,
                        (joint_style==gfx_joinMiter)?ART_PATH_STROKE_JOIN_MITER:
                        ((joint_style==gfx_joinRound)?ART_PATH_STROKE_JOIN_ROUND:
@@ -368,11 +491,27 @@ gfxpoly_t* gfxpoly_strokeToPoly(gfxline_t*line, gfxcoord_t width, gfx_capType ca
 
 gfxline_t* gfxline_circularToEvenOdd(gfxline_t*line)
 {
 
 gfxline_t* gfxline_circularToEvenOdd(gfxline_t*line)
 {
+    msg("<verbose> Converting circular-filled gfxline of %d segments to even-odd filled gfxline", gfxline_len(line));
     ArtSVP* svp = gfxfillToSVP(line, 1);
     ArtSVP* svp = gfxfillToSVP(line, 1);
-    ArtSVP* svp2 = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_POSITIVE);
-    gfxline_t* result = gfxpoly_to_gfxline((gfxpoly_t*)svp2);
+
+    /* TODO: ART_WIND_RULE_POSITIVE means that a shape is visible if
+             positive and negative line segments add up to something positive.
+             I *think* that clockwise fill in PDF is defined in a way, however,
+             that the *last* shape's direction will determine whether something
+             is filled */
+    ArtSVP* svp_rewinded;
+#ifdef ART_USE_NEW_INTERSECTOR
+    ArtSvpWriter * swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
+    art_svp_intersector(svp, swr);
+    svp_rewinded = art_svp_writer_rewind_reap(swr);
+#else
+    error
+    svp_rewinded = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_POSITIVE);
+#endif
+
+    gfxline_t* result = gfxpoly_to_gfxline((gfxpoly_t*)svp_rewinded);
     art_svp_free(svp);
     art_svp_free(svp);
-    art_svp_free(svp2);
+    art_svp_free(svp_rewinded);
     return result;
 }
 
     return result;
 }