made gfxpoly_check test for correct circular filling
[swftools.git] / lib / gfxpoly / poly.c
index 8279b11..9ffc770 100644 (file)
@@ -165,10 +165,11 @@ int gfxpoly_size(gfxpoly_t*poly)
     return edges;
 }
 
-char gfxpoly_check(gfxpoly_t*poly)
+char gfxpoly_check(gfxpoly_t*poly, char updown)
 {
     current_polygon = poly;
-    dict_t*d = dict_new2(&point_type);
+    dict_t*d1 = dict_new2(&point_type);
+    dict_t*d2 = dict_new2(&point_type);
     int s,t;
     gfxpolystroke_t*stroke = poly->strokes;
     for(;stroke;stroke=stroke->next) {
@@ -180,26 +181,54 @@ char gfxpoly_check(gfxpoly_t*poly)
           that the endpoint multiplicity is two */
        for(s=0;s<stroke->num_points;s++) {
            point_t p = stroke->points[s];
-           int num = (s>=1 && s<stroke->num_points-1)?2:1; // mid points are two points (start+end)
-           if(!dict_contains(d, &p)) {
-               dict_put(d, &p, (void*)(ptroff_t)num);
+           int num_xor = (s>=1 && s<stroke->num_points-1)?2:1; // mid points are two points (start+end)
+           int num_circ = (s>=1 && s<stroke->num_points-1)?0:(s==0?1:-1);
+           if(stroke->dir==DIR_UP)
+               num_circ=-num_circ;
+
+           if(!dict_contains(d1, &p)) {
+               dict_put(d1, &p, (void*)(ptroff_t)num_xor);
+               if(updown) {
+                   assert(!dict_contains(d2, &p));
+                   dict_put(d2, &p, (void*)(ptroff_t)num_circ);
+               }
            } else {
-               int count = (ptroff_t)dict_lookup(d, &p);
-               dict_del(d, &p);
-               count+=num;
-               dict_put(d, &p, (void*)(ptroff_t)count);
+               int count = (ptroff_t)dict_lookup(d1, &p);
+               dict_del(d1, &p);
+               count+=num_xor;
+               dict_put(d1, &p, (void*)(ptroff_t)count);
+
+               if(updown) {
+                   assert(dict_contains(d2, &p));
+                   count = (ptroff_t)dict_lookup(d2, &p);
+                   dict_del(d2, &p);
+                   count+=num_circ;
+                   dict_put(d2, &p, (void*)(ptroff_t)count);
+               }
            }
        }
     }
-    DICT_ITERATE_ITEMS(d, point_t*, p, void*, c) {
-        int count = (ptroff_t)c;
+    DICT_ITERATE_ITEMS(d1, point_t*, p1, void*, c1) {
+        int count = (ptroff_t)c1;
         if(count&1) {
-            fprintf(stderr, "Point (%d,%d) occurs %d times\n", p->x, p->y, count);
-            dict_destroy(d);
-           assert(count%2 == 0);
+            fprintf(stderr, "Point (%d,%d) occurs %d times\n", p1->x, p1->y, count);
+            dict_destroy(d1);
+           return 0;
         }
     }
-    dict_destroy(d);
+    if(updown) {
+       DICT_ITERATE_ITEMS(d2, point_t*, p2, void*, c2) {
+           int count = (ptroff_t)c2;
+           if(count!=0) {
+               if(count>0) fprintf(stderr, "Point (%d,%d) has %d more incoming than outgoing segments\n", p2->x, p2->y, count);
+               if(count<0) fprintf(stderr, "Point (%d,%d) has %d more outgoing than incoming segments\n", p2->x, p2->y, -count);
+               dict_destroy(d2);
+               return 0;
+           }
+       }
+    }
+    dict_destroy(d1);
+    dict_destroy(d2);
     return 1;
 }
 
@@ -210,12 +239,21 @@ void gfxpoly_dump(gfxpoly_t*poly)
     fprintf(stderr, "polyon %p (gridsize: %f)\n", poly, poly->gridsize);
     gfxpolystroke_t*stroke = poly->strokes;
     for(;stroke;stroke=stroke->next) {
-       fprintf(stderr, "%p", stroke);
-       for(s=0;s<stroke->num_points-1;s++) {
-           point_t a = stroke->points[s];
-           point_t b = stroke->points[s+1];
-           fprintf(stderr, "%s (%f,%f) -> (%f,%f)%s\n", s?"           ":"", a.x*g, a.y*g, b.x*g, b.y*g,
-                                                       s==stroke->num_points-2?"]":"");
+       fprintf(stderr, "%11p", stroke);
+       if(stroke->dir==DIR_UP) {
+           for(s=stroke->num_points-1;s>=1;s--) {
+               point_t a = stroke->points[s];
+               point_t b = stroke->points[s-1];
+               fprintf(stderr, "%s (%f,%f) -> (%f,%f)%s%s\n", s!=stroke->num_points-1?"           ":"", a.x*g, a.y*g, b.x*g, b.y*g,
+                                                           s==1?"]":"", a.y==b.y?"H":"");
+           }
+       } else {
+           for(s=0;s<stroke->num_points-1;s++) {
+               point_t a = stroke->points[s];
+               point_t b = stroke->points[s+1];
+               fprintf(stderr, "%s (%f,%f) -> (%f,%f)%s%s\n", s?"           ":"", a.x*g, a.y*g, b.x*g, b.y*g,
+                                                           s==stroke->num_points-2?"]":"", a.y==b.y?"H":"");
+           }
        }
     }
 }
@@ -241,6 +279,50 @@ void gfxpoly_save(gfxpoly_t*poly, const char*filename)
     fclose(fi);
 }
 
+void gfxpoly_save_arrows(gfxpoly_t*poly, const char*filename)
+{
+    FILE*fi = fopen(filename, "wb");
+    fprintf(fi, "%% gridsize %f\n", poly->gridsize);
+    fprintf(fi, "%% begin\n");
+    int t;
+    double l = 5.0 / poly->gridsize;
+    double g = poly->gridsize;
+    gfxpolystroke_t*stroke = poly->strokes;
+    for(;stroke;stroke=stroke->next) {
+       fprintf(fi, "%g setgray\n", 0);
+
+       int s = stroke->dir==DIR_UP?stroke->num_points-1:0;
+       int end = stroke->dir==DIR_UP?-1:stroke->num_points;
+       int dir = stroke->dir==DIR_UP?-1:1;
+
+       point_t p = stroke->points[s];
+       s+=dir;
+       point_t o = p;
+       fprintf(fi, "%f %f moveto\n", p.x * g, p.y * g);
+       for(;s!=end;s+=dir) {
+           p = stroke->points[s];
+           int lx = p.x - o.x;
+           int ly = p.y - o.y;
+           double d = sqrt(lx*lx+ly*ly);
+           if(!d) d=1;
+           else   d = l / d;
+           double d2 = d*1.5;
+           fprintf(fi, "%f %f lineto\n", (p.x - lx*d2) * g, (p.y - ly*d2) * g);
+           fprintf(fi, "%f %f lineto\n", (p.x - lx*d2 + (ly*d))*g,
+                                         (p.y - ly*d2 - (lx*d))*g);
+           fprintf(fi, "%f %f lineto\n", p.x * g, p.y * g);
+           fprintf(fi, "%f %f lineto\n", (p.x - lx*d2 - (ly*d))*g, 
+                                         (p.y - ly*d2 + (lx*d))*g);
+           fprintf(fi, "%f %f lineto\n", (p.x - lx*d2) * g, (p.y - ly*d2) * g);
+           fprintf(fi, "%f %f moveto\n", p.x * g, p.y * g);
+           o = p;
+       }
+       fprintf(fi, "stroke\n");
+    }
+    fprintf(fi, "showpage\n");
+    fclose(fi);
+}
+
 inline static event_t* event_new()
 {
     event_t*e = rfx_calloc(sizeof(event_t));
@@ -1145,17 +1227,12 @@ static void add_horizontals(gfxpoly_t*poly, windrule_t*windrule, windcontext_t*c
         actlist_verify(actlist, y-1);
 #endif
        edgestyle_t*fill = 0;
-       char dir_up = 0;
-       char dir_down = 0;
+       int wind = 0;
 
         do {
            assert(e->s1->fs);
             if(fill && x != e->p.x) {
-               assert(!dir_up || !dir_down);
-               assert(dir_up || dir_down);
-#ifdef DEBUG
-                fprintf(stderr, "%d) draw horizontal line from %d to %d\n", y, x, e->p.x);
-#endif
+               assert(abs(wind)==1);
                assert(x<e->p.x);
 
                 gfxpolystroke_t*stroke = rfx_calloc(sizeof(gfxpolystroke_t));
@@ -1164,22 +1241,28 @@ static void add_horizontals(gfxpoly_t*poly, windrule_t*windrule, windcontext_t*c
 
                stroke->num_points = 2;
                stroke->points = malloc(sizeof(point_t)*2);
-               stroke->dir = dir_up?DIR_UP:DIR_DOWN;
+
+               if(wind>0) {
+                   stroke->dir = DIR_DOWN;
+               } else {
+                   stroke->dir = DIR_UP;
+               }
+#ifdef DEBUG
+                fprintf(stderr, "%d) draw horizontal line from %d to %d (dir=%s)\n", y, x, e->p.x, stroke->dir==DIR_UP?"up":"down");
+#endif
+
                stroke->fs = fill;
                point_t a,b;
                 a.y = b.y = y;
-               /* we draw from low x to high x so that left/right fillstyles add up
-                   (because the horizontal line's fill style controls the area *below* the line)
-                */
-                a.x = e->p.x;
-                b.x = x;
+                a.x = x;
+                b.x = e->p.x;
                stroke->points[0] = a;
                stroke->points[1] = b;
 #ifdef CHECKS
                /* the output should always be intersection free polygons, so check this horizontal
                   line isn't puncturing any segments in the active list */
-               segment_t* start = actlist_find(actlist, b, b);
-               segment_t* s = actlist_find(actlist, a, a);
+               segment_t* start = actlist_find(actlist, a, a);
+               segment_t* s = actlist_find(actlist, b, b);
                while(s!=start) {
                    assert(s->a.y == y || s->b.y == y);
                    s = s->left;
@@ -1201,14 +1284,14 @@ static void add_horizontals(gfxpoly_t*poly, windrule_t*windrule, windcontext_t*c
                     e->s2 = 0;
                     hqueue_put(&hqueue, e);
                     left = actlist_left(actlist, s);
-                   dir_down^=1;
+                   wind += e->s1->dir==DIR_DOWN?-1:1;
                     break;
                 }
                 case EVENT_END: {
                     left = actlist_left(actlist, s);
                     actlist_delete(actlist, s);
                    advance_stroke(0, &hqueue, s->stroke, s->polygon_nr, s->stroke_pos);
-                   dir_up^=1;
+                   wind += e->s1->dir==DIR_DOWN?1:-1;
                     break;
                 }
                 default: assert(0);
@@ -1240,11 +1323,11 @@ static void add_horizontals(gfxpoly_t*poly, windrule_t*windrule, windcontext_t*c
 #endif
 
 #ifdef DEBUG
-            fprintf(stderr, "%d) event=%s[%d] left:[%d] x:%d\n",
+            fprintf(stderr, "%d) event=%s[%d] left:[%d] x:%d dir:%s\n",
                     y, e->type==EVENT_START?"start":"end",
                     s->nr,
                     left?left->nr:-1,
-                    x);
+                    x, s->dir==DIR_UP?"up":"down");
 #endif
 
             if(e->type == EVENT_END)
@@ -1257,6 +1340,13 @@ static void add_horizontals(gfxpoly_t*poly, windrule_t*windrule, windcontext_t*c
 #ifdef CHECKS
        edgestyle_t*bleeding = fill;
         assert(!bleeding);
+       segment_t*s = actlist_leftmost(actlist);
+       int dir = 0;
+       while(s) {
+           dir += s->dir==DIR_UP?-1:1;
+           s = actlist_right(actlist, s);
+       }
+       assert(!dir);
 #endif
     }